aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--HOWTO/INSTALL-ANDROID.md52
-rw-r--r--HOWTO/INSTALL-CROSS.md14
-rw-r--r--HOWTO/INSTALL-WIN32.md10
-rw-r--r--HOWTO/INSTALL.md895
-rw-r--r--Makefile.in13
-rw-r--r--OTP_VERSION2
-rw-r--r--README.md11
-rw-r--r--aclocal.m410
-rw-r--r--bootstrap/bin/start.bootbin5264 -> 5259 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin5264 -> 5259 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11676 -> 11672 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bool.beambin16240 -> 16340 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_disasm.beambin26316 -> 26320 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_jump.beambin8680 -> 8700 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin13460 -> 13476 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin30976 -> 32024 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_clauses.beambin2876 -> 2956 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_inline.beambin38880 -> 39100 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_trees.beambin20368 -> 20368 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin38852 -> 39832 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.app6
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.appup2
-rw-r--r--bootstrap/lib/compiler/ebin/core_lib.beambin5740 -> 5980 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_lint.beambin12196 -> 12820 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_parse.beambin42816 -> 42968 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_pp.beambin12776 -> 13196 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/erl_bifs.beambin2168 -> 2180 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin49840 -> 50208 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_pre_expand.beambin14216 -> 14244 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_codegen.beambin54480 -> 54516 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin51748 -> 53472 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin46996 -> 47460 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel_pp.beambin12376 -> 12644 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_life.beambin20708 -> 20696 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application.beambin4676 -> 4524 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_server.beambin6604 -> 6668 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_util.beambin10512 -> 10512 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global.beambin32672 -> 32688 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin13256 -> 13516 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_config.beambin7784 -> 7812 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app3
-rw-r--r--bootstrap/lib/kernel/ebin/os.beambin5448 -> 5716 bytes
-rw-r--r--bootstrap/lib/kernel/include/dist.hrl3
-rw-r--r--bootstrap/lib/stdlib/ebin/dets.beambin53624 -> 53656 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin27608 -> 28428 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_compile.beambin7312 -> 7324 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_eval.beambin30648 -> 30708 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_expand_records.beambin22504 -> 22504 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin86612 -> 89740 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin74708 -> 75044 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin26316 -> 26656 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_event.beambin18264 -> 18320 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_fsm.beambin15628 -> 15652 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_server.beambin17404 -> 17416 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/otp_internal.beambin10436 -> 10172 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/pg.beambin2064 -> 2096 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin73660 -> 73720 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/slave.beambin4860 -> 5200 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.app5
-rw-r--r--bootstrap/lib/stdlib/ebin/sys.beambin7556 -> 8372 bytes
-rw-r--r--configure.in4
-rw-r--r--erts/Makefile.in5
-rw-r--r--erts/aclocal.m410
-rw-r--r--erts/configure.in66
-rw-r--r--erts/doc/src/erl.xml11
-rw-r--r--erts/doc/src/erl_driver.xml7
-rw-r--r--erts/doc/src/erl_ext_dist.xml29
-rw-r--r--erts/doc/src/erl_nif.xml26
-rw-r--r--erts/doc/src/erlang.xml35
-rw-r--r--erts/doc/src/escript.xml32
-rw-r--r--erts/doc/src/notes.xml732
-rw-r--r--erts/doc/src/time_correction.xml4
-rw-r--r--erts/emulator/Makefile.in25
-rw-r--r--erts/emulator/beam/atom.names2
-rw-r--r--erts/emulator/beam/beam_bp.c5
-rw-r--r--erts/emulator/beam/beam_emu.c4
-rw-r--r--erts/emulator/beam/bif.c7
-rw-r--r--erts/emulator/beam/bif.h118
-rw-r--r--erts/emulator/beam/bif.tab9
-rw-r--r--erts/emulator/beam/binary.c1157
-rw-r--r--erts/emulator/beam/copy.c3
-rw-r--r--erts/emulator/beam/dist.c64
-rw-r--r--erts/emulator/beam/dist.h6
-rw-r--r--erts/emulator/beam/erl_alloc.h2
-rw-r--r--erts/emulator/beam/erl_async.c6
-rw-r--r--erts/emulator/beam/erl_bif_binary.c11
-rw-r--r--erts/emulator/beam/erl_bif_ddll.c6
-rwxr-xr-xerts/emulator/beam/erl_bif_info.c5
-rw-r--r--erts/emulator/beam/erl_bif_port.c2
-rw-r--r--erts/emulator/beam/erl_binary.h2
-rw-r--r--erts/emulator/beam/erl_db.c62
-rw-r--r--erts/emulator/beam/erl_db_tree.c4
-rw-r--r--erts/emulator/beam/erl_driver.h18
-rw-r--r--erts/emulator/beam/erl_init.c2
-rw-r--r--erts/emulator/beam/erl_lock_check.c4
-rw-r--r--erts/emulator/beam/erl_map.c32
-rw-r--r--erts/emulator/beam/erl_message.c43
-rw-r--r--erts/emulator/beam/erl_nif.c58
-rw-r--r--erts/emulator/beam/erl_nif.h12
-rw-r--r--erts/emulator/beam/erl_port_task.c4
-rw-r--r--erts/emulator/beam/erl_process.c26
-rw-r--r--erts/emulator/beam/erl_term.h12
-rw-r--r--erts/emulator/beam/erl_trace.c23
-rw-r--r--erts/emulator/beam/erl_trace.h1
-rw-r--r--erts/emulator/beam/erl_unicode.c2
-rw-r--r--erts/emulator/beam/external.c199
-rwxr-xr-xerts/emulator/beam/global.h53
-rw-r--r--erts/emulator/beam/io.c18
-rw-r--r--erts/emulator/beam/ops.tab3
-rw-r--r--erts/emulator/beam/sys.h6
-rw-r--r--erts/emulator/beam/utils.c496
-rw-r--r--erts/emulator/drivers/common/gzio.c3
-rw-r--r--erts/emulator/drivers/common/inet_drv.c595
-rw-r--r--erts/emulator/drivers/common/zlib_drv.c3
-rw-r--r--erts/emulator/drivers/unix/unix_efile.c7
-rw-r--r--erts/emulator/drivers/win32/win_efile.c765
-rw-r--r--erts/emulator/hipe/hipe_bif0.c39
-rw-r--r--erts/emulator/hipe/hipe_bif0.tab3
-rw-r--r--erts/emulator/hipe/hipe_bif2.c7
-rw-r--r--erts/emulator/hipe/hipe_bif2.tab1
-rw-r--r--erts/emulator/hipe/hipe_bif_list.m413
-rw-r--r--erts/emulator/hipe/hipe_x86_signal.c6
-rw-r--r--erts/emulator/sys/common/erl_check_io.c3
-rw-r--r--erts/emulator/sys/ose/beam.lmconf (renamed from erts/emulator/sys/ose/default.lmconf)3
-rw-r--r--erts/emulator/sys/ose/erl_main.c2
-rw-r--r--erts/emulator/sys/ose/erl_poll.c8
-rw-r--r--erts/emulator/sys/ose/sys.c353
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c46
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys_ddll.c4
-rw-r--r--erts/emulator/sys/unix/sys.c11
-rw-r--r--erts/emulator/sys/win32/erl_win_sys.h20
-rw-r--r--erts/emulator/test/binary_SUITE.erl276
-rw-r--r--erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c4
-rw-r--r--erts/emulator/test/erts_debug_SUITE.erl6
-rw-r--r--erts/emulator/test/map_SUITE.erl38
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl14
-rw-r--r--erts/emulator/test/nif_SUITE.erl23
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c32
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_mod.c3
-rw-r--r--erts/emulator/test/port_SUITE.erl9
-rw-r--r--erts/emulator/test/system_info_SUITE.erl1
-rw-r--r--erts/emulator/test/trace_SUITE.erl22
-rw-r--r--erts/emulator/test/trace_call_time_SUITE.erl96
-rwxr-xr-xerts/emulator/utils/gen_git_version6
-rw-r--r--erts/epmd/src/Makefile.in4
-rw-r--r--erts/epmd/src/epmd.c18
-rw-r--r--erts/epmd/src/epmd_int.h6
-rw-r--r--erts/epmd/src/epmd_srv.c46
-rw-r--r--erts/etc/common/Makefile.in4
-rw-r--r--erts/etc/common/to_erl_common.c3
-rw-r--r--erts/etc/ose/etc.lmconf20
-rw-r--r--erts/etc/ose/run_erl_main.c2
-rw-r--r--erts/etc/unix/etp-commands.in241
-rw-r--r--erts/etc/win32/nsis/erlang.nsi1
-rw-r--r--erts/etc/win32/nsis/erlang20.nsi1
-rw-r--r--erts/include/internal/ethr_mutex.h2
-rw-r--r--erts/include/internal/ethread.h18
-rw-r--r--erts/lib_src/pthread/ethread.c6
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin54792 -> 54768 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin98276 -> 97732 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin4296 -> 4176 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin48816 -> 48780 bytes
-rw-r--r--erts/preloaded/ebin/otp_ring0.beambin1480 -> 1460 bytes
-rw-r--r--erts/preloaded/ebin/prim_eval.beambin1360 -> 1340 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin44916 -> 44892 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin72932 -> 72912 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin23448 -> 23424 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin13196 -> 13176 bytes
-rw-r--r--erts/preloaded/src/erlang.erl42
-rw-r--r--erts/preloaded/src/erts.app.src3
-rw-r--r--erts/preloaded/src/erts_internal.erl21
-rw-r--r--erts/preloaded/src/init.erl2
-rw-r--r--erts/start_scripts/no_dot_erlang.rel.src4
-rw-r--r--erts/start_scripts/start_all_example.rel.src4
-rw-r--r--erts/start_scripts/start_clean.rel.src4
-rw-r--r--erts/start_scripts/start_sasl.rel.src4
-rw-r--r--erts/test/otp_SUITE.erl130
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/asn1/doc/src/asn1_ug.xml796
-rw-r--r--lib/asn1/doc/src/asn1ct.xml9
-rw-r--r--lib/asn1/doc/src/notes.xml106
-rw-r--r--lib/asn1/src/asn1.app.src3
-rw-r--r--lib/asn1/src/asn1ct.erl2
-rw-r--r--lib/asn1/src/asn1ct_check.erl219
-rw-r--r--lib/asn1/src/asn1ct_gen.erl19
-rw-r--r--lib/asn1/test/asn1_SUITE.erl8
-rw-r--r--lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn14
-rw-r--r--lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn114
-rw-r--r--lib/asn1/test/asn1_SUITE_data/ImportsFrom2.asn17
-rw-r--r--lib/asn1/test/asn1_SUITE_data/ImportsFrom3.asn14
-rw-r--r--lib/asn1/test/error_SUITE.erl81
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/doc/src/ct_hooks.xml50
-rw-r--r--lib/common_test/doc/src/ct_run.xml3
-rw-r--r--lib/common_test/doc/src/event_handler_chapter.xml29
-rw-r--r--lib/common_test/doc/src/notes.xml178
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml18
-rw-r--r--lib/common_test/src/common_test.app.src7
-rw-r--r--lib/common_test/src/ct.erl5
-rw-r--r--lib/common_test/src/ct_conn_log_h.erl9
-rw-r--r--lib/common_test/src/ct_framework.erl74
-rw-r--r--lib/common_test/src/ct_gen_conn.erl7
-rw-r--r--lib/common_test/src/ct_hooks.erl38
-rw-r--r--lib/common_test/src/ct_netconfc.erl27
-rw-r--r--lib/common_test/src/ct_run.erl65
-rw-r--r--lib/common_test/src/ct_telnet.erl334
-rw-r--r--lib/common_test/src/ct_telnet_client.erl137
-rw-r--r--lib/common_test/src/ct_testspec.erl7
-rw-r--r--lib/common_test/src/ct_util.erl25
-rw-r--r--lib/common_test/src/ct_util.hrl1
-rw-r--r--lib/common_test/src/cth_conn_log.erl12
-rw-r--r--lib/common_test/src/cth_surefire.erl4
-rw-r--r--lib/common_test/src/unix_telnet.erl29
-rw-r--r--lib/common_test/test/ct_config_info_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl52
-rw-r--r--lib/common_test/test/ct_group_info_SUITE.erl30
-rw-r--r--lib/common_test/test/ct_groups_spec_SUITE.erl75
-rw-r--r--lib/common_test/test/ct_hooks_SUITE.erl2
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl14
-rw-r--r--lib/common_test/test/ct_master_SUITE.erl3
-rw-r--r--lib/common_test/test/ct_repeat_1_SUITE.erl35
-rw-r--r--lib/common_test/test/ct_repeat_testrun_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_sequence_1_SUITE.erl19
-rw-r--r--lib/common_test/test/ct_skip_SUITE.erl118
-rw-r--r--lib/common_test/test/ct_surefire_SUITE.erl2
-rw-r--r--lib/common_test/test/ct_telnet_SUITE.erl60
-rw-r--r--lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl105
-rw-r--r--lib/common_test/test/ct_test_server_if_1_SUITE.erl8
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE.erl92
-rw-r--r--lib/common_test/test/telnet_server.erl108
-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.xml227
-rw-r--r--lib/compiler/src/beam_bool.erl29
-rw-r--r--lib/compiler/src/beam_disasm.erl9
-rw-r--r--lib/compiler/src/beam_jump.erl2
-rw-r--r--lib/compiler/src/beam_utils.erl2
-rw-r--r--lib/compiler/src/cerl.erl115
-rw-r--r--lib/compiler/src/cerl_clauses.erl23
-rw-r--r--lib/compiler/src/cerl_inline.erl25
-rw-r--r--lib/compiler/src/cerl_trees.erl8
-rw-r--r--lib/compiler/src/compile.erl61
-rw-r--r--lib/compiler/src/compiler.app.src4
-rw-r--r--lib/compiler/src/core_lib.erl13
-rw-r--r--lib/compiler/src/core_lint.erl27
-rw-r--r--lib/compiler/src/core_parse.hrl20
-rw-r--r--lib/compiler/src/core_parse.yrl8
-rw-r--r--lib/compiler/src/core_pp.erl18
-rw-r--r--lib/compiler/src/erl_bifs.erl1
-rw-r--r--lib/compiler/src/sys_core_fold.erl95
-rw-r--r--lib/compiler/src/sys_pre_expand.erl9
-rw-r--r--lib/compiler/src/v3_codegen.erl6
-rw-r--r--lib/compiler/src/v3_core.erl222
-rw-r--r--lib/compiler/src/v3_kernel.erl39
-rw-r--r--lib/compiler/src/v3_kernel_pp.erl18
-rw-r--r--lib/compiler/src/v3_life.erl3
-rw-r--r--lib/compiler/test/andor_SUITE.erl17
-rw-r--r--lib/compiler/test/core_SUITE.erl5
-rw-r--r--lib/compiler/test/core_SUITE_data/bad_boolean_guard.core32
-rw-r--r--lib/compiler/test/core_SUITE_data/map_core_test.core4
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl23
-rw-r--r--lib/compiler/test/error_SUITE.erl28
-rw-r--r--lib/compiler/test/fun_SUITE.erl8
-rw-r--r--lib/compiler/test/guard_SUITE.erl12
-rw-r--r--lib/compiler/test/lc_SUITE.erl8
-rw-r--r--lib/compiler/test/map_SUITE.erl79
-rw-r--r--lib/compiler/test/receive_SUITE.erl1
-rw-r--r--lib/compiler/test/record_SUITE.erl8
-rw-r--r--lib/compiler/test/warnings_SUITE.erl116
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/cosEvent/doc/src/notes.xml18
-rw-r--r--lib/cosEvent/src/cosEvent.app.src3
-rw-r--r--lib/cosEvent/vsn.mk2
-rw-r--r--lib/cosEventDomain/doc/src/notes.xml18
-rw-r--r--lib/cosEventDomain/src/cosEventDomain.app.src4
-rw-r--r--lib/cosEventDomain/vsn.mk2
-rw-r--r--lib/cosFileTransfer/doc/src/notes.xml18
-rw-r--r--lib/cosFileTransfer/src/cosFileTransfer.app.src4
-rw-r--r--lib/cosFileTransfer/vsn.mk2
-rw-r--r--lib/cosNotification/doc/src/notes.xml18
-rw-r--r--lib/cosNotification/src/cosNotification.app.src4
-rw-r--r--lib/cosNotification/vsn.mk2
-rw-r--r--lib/cosProperty/doc/src/notes.xml18
-rw-r--r--lib/cosProperty/src/cosProperty.app.src4
-rw-r--r--lib/cosProperty/vsn.mk2
-rw-r--r--lib/cosTime/doc/src/notes.xml18
-rw-r--r--lib/cosTime/src/cosTime.app.src4
-rw-r--r--lib/cosTime/vsn.mk3
-rw-r--r--lib/cosTransactions/doc/src/notes.xml18
-rw-r--r--lib/cosTransactions/src/cosTransactions.app.src3
-rw-r--r--lib/cosTransactions/vsn.mk2
-rw-r--r--lib/crypto/c_src/crypto.c33
-rw-r--r--lib/crypto/doc/src/crypto.xml4
-rw-r--r--lib/crypto/doc/src/crypto_app.xml4
-rw-r--r--lib/crypto/doc/src/notes.xml104
-rw-r--r--lib/crypto/src/crypto.app.src3
-rw-r--r--lib/crypto/src/crypto.erl26
-rw-r--r--lib/crypto/test/crypto_SUITE.erl24
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/doc/src/notes.xml66
-rw-r--r--lib/debugger/src/dbg_ieval.erl16
-rw-r--r--lib/debugger/src/dbg_iload.erl13
-rw-r--r--lib/debugger/src/debugger.app.src4
-rw-r--r--lib/debugger/src/debugger.erl6
-rw-r--r--lib/debugger/test/int_eval_SUITE.erl10
-rw-r--r--lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl6
-rw-r--r--lib/debugger/test/map_SUITE.erl15
-rw-r--r--lib/debugger/vsn.mk2
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml109
-rw-r--r--lib/dialyzer/doc/src/notes.xml167
-rw-r--r--lib/dialyzer/src/Makefile2
-rw-r--r--lib/dialyzer/src/dialyzer.app.src5
-rw-r--r--lib/dialyzer/src/dialyzer.erl16
-rw-r--r--lib/dialyzer/src/dialyzer.hrl15
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl10
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl48
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl13
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl87
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl51
-rw-r--r--lib/dialyzer/src/dialyzer_dep.erl10
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl33
-rw-r--r--lib/dialyzer/src/dialyzer_options.erl7
-rw-r--r--lib/dialyzer/src/dialyzer_races.erl28
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/modules/opaque_erl_scan.erl2
-rw-r--r--lib/dialyzer/test/options1_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/race_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes39
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes23
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/funs_from_outside7
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/invalid_spec_22
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/maps_difftype3
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl47
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes2.erl40
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/ddfs_master/common_types.hrl6
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/ddfs_master/config.hrl148
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs.hrl9
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_gc.hrl17
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_master.erl531
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_tag.hrl19
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/ddfs_master/gs_util.hrl16
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/funs_from_outside.erl83
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_data.erl5
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_user.erl8
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/maps_difftype.erl11
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/maps_redef.erl12
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/predef.erl4
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/remote_field.erl11
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/remote_field2.erl17
-rw-r--r--lib/dialyzer/test/underspecs_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/user_SUITE_data/dialyzer_options2
-rw-r--r--lib/diameter/doc/src/diameter.xml2
-rw-r--r--lib/diameter/doc/src/diameter_make.xml2
-rw-r--r--lib/diameter/doc/src/notes.xml168
-rw-r--r--lib/diameter/examples/code/client.erl2
-rw-r--r--lib/diameter/examples/code/client_cb.erl14
-rw-r--r--lib/diameter/examples/code/redirect_cb.erl8
-rw-r--r--lib/diameter/examples/code/relay_cb.erl8
-rw-r--r--lib/diameter/examples/code/server_cb.erl53
-rw-r--r--lib/diameter/include/diameter.hrl11
-rw-r--r--lib/diameter/include/diameter_gen.hrl117
-rw-r--r--lib/diameter/src/Makefile24
-rw-r--r--lib/diameter/src/app.sed40
-rw-r--r--lib/diameter/src/base/diameter_codec.erl196
-rw-r--r--lib/diameter/src/base/diameter_config.erl4
-rw-r--r--lib/diameter/src/base/diameter_lib.erl56
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl158
-rw-r--r--lib/diameter/src/base/diameter_service.erl86
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl215
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl89
-rw-r--r--lib/diameter/src/compiler/diameter_codegen.erl8
-rw-r--r--lib/diameter/src/compiler/diameter_dict_util.erl9
-rw-r--r--lib/diameter/src/compiler/diameter_forms.hrl6
-rw-r--r--lib/diameter/src/compiler/diameter_make.erl32
-rw-r--r--lib/diameter/src/diameter.app.src26
-rw-r--r--lib/diameter/src/diameter.appup.src28
-rw-r--r--lib/diameter/src/info/diameter_dbg.erl (renamed from lib/diameter/src/base/diameter_dbg.erl)163
-rw-r--r--lib/diameter/src/info/diameter_info.erl (renamed from lib/diameter/src/base/diameter_info.erl)14
-rw-r--r--lib/diameter/src/modules.mk12
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl2
-rw-r--r--lib/diameter/test/diameter_app_SUITE.erl66
-rw-r--r--lib/diameter/test/diameter_codec_test.erl3
-rw-r--r--lib/diameter/test/diameter_compiler_SUITE.erl25
-rw-r--r--lib/diameter/test/diameter_dpr_SUITE.erl8
-rw-r--r--lib/diameter/test/diameter_examples_SUITE.erl85
-rw-r--r--lib/diameter/test/diameter_failover_SUITE.erl8
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl85
-rw-r--r--lib/diameter/vsn.mk2
-rw-r--r--lib/edoc/doc/src/notes.xml29
-rw-r--r--lib/edoc/src/edoc.app.src4
-rw-r--r--lib/edoc/src/edoc_types.erl1
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/eldap/doc/src/notes.xml46
-rw-r--r--lib/eldap/src/Makefile2
-rw-r--r--lib/eldap/src/eldap.app.src4
-rw-r--r--lib/eldap/test/eldap_misc_SUITE.erl51
-rw-r--r--lib/erl_docgen/doc/src/notes.xml43
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl17
-rw-r--r--lib/erl_docgen/src/docgen_otp_specs.erl2
-rw-r--r--lib/erl_docgen/src/erl_docgen.app.src4
-rw-r--r--lib/erl_docgen/vsn.mk2
-rw-r--r--lib/erl_interface/aclocal.m410
-rw-r--r--lib/erl_interface/doc/src/ei.xml34
-rw-r--r--lib/erl_interface/doc/src/notes.xml16
-rw-r--r--lib/erl_interface/include/ei.h8
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c9
-rw-r--r--lib/erl_interface/src/connect/ei_connect_int.h3
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.c2
-rw-r--r--lib/erl_interface/src/decode/decode_skip.c10
-rw-r--r--lib/erl_interface/src/decode/decode_tuple_header.c23
-rw-r--r--lib/erl_interface/src/encode/encode_tuple_header.c19
-rw-r--r--lib/erl_interface/src/misc/ei_decode_term.c3
-rw-r--r--lib/erl_interface/src/misc/ei_x_encode.c12
-rw-r--r--lib/erl_interface/test/all_SUITE_data/ei_runner.c6
-rw-r--r--lib/erl_interface/test/all_SUITE_data/ei_runner.h3
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE.erl11
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c490
-rw-r--r--lib/erl_interface/vsn.mk3
-rw-r--r--lib/et/doc/src/notes.xml45
-rw-r--r--lib/et/src/et.app.src4
-rw-r--r--lib/et/vsn.mk2
-rw-r--r--lib/eunit/doc/src/notes.xml29
-rw-r--r--lib/eunit/src/eunit.app.src3
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/gs/doc/src/notes.xml31
-rw-r--r--lib/gs/src/gs.app.src3
-rw-r--r--lib/gs/vsn.mk2
-rw-r--r--lib/hipe/Makefile2
-rw-r--r--lib/hipe/arm/hipe_arm_assemble.erl48
-rw-r--r--lib/hipe/cerl/Makefile11
-rw-r--r--lib/hipe/cerl/cerl_prettypr.erl24
-rw-r--r--lib/hipe/cerl/cerl_to_icode.erl38
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl13
-rw-r--r--lib/hipe/cerl/erl_types.erl136
-rw-r--r--lib/hipe/doc/src/notes.xml118
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl143
-rw-r--r--lib/hipe/icode/hipe_icode.erl13
-rw-r--r--lib/hipe/icode/hipe_icode.hrl8
-rw-r--r--lib/hipe/icode/hipe_icode_fp.erl2
-rw-r--r--lib/hipe/icode/hipe_icode_mulret.erl16
-rw-r--r--lib/hipe/icode/hipe_icode_type.erl47
-rw-r--r--lib/hipe/llvm/Makefile109
-rw-r--r--lib/hipe/llvm/elf32_format.hrl59
-rw-r--r--lib/hipe/llvm/elf64_format.hrl58
-rw-r--r--lib/hipe/llvm/elf_format.erl790
-rw-r--r--lib/hipe/llvm/elf_format.hrl488
-rw-r--r--lib/hipe/llvm/hipe_llvm.erl1131
-rw-r--r--lib/hipe/llvm/hipe_llvm_arch.hrl11
-rw-r--r--lib/hipe/llvm/hipe_llvm_liveness.erl112
-rw-r--r--lib/hipe/llvm/hipe_llvm_main.erl506
-rw-r--r--lib/hipe/llvm/hipe_llvm_merge.erl114
-rw-r--r--lib/hipe/llvm/hipe_rtl_to_llvm.erl1613
-rw-r--r--lib/hipe/main/hipe.app.src10
-rw-r--r--lib/hipe/main/hipe.erl85
-rw-r--r--lib/hipe/main/hipe_main.erl42
-rw-r--r--lib/hipe/misc/hipe_gensym.erl2
-rw-r--r--lib/hipe/misc/hipe_pack_constants.erl100
-rw-r--r--lib/hipe/ppc/hipe_ppc_assemble.erl48
-rw-r--r--lib/hipe/regalloc/hipe_ls_regalloc.erl4
-rw-r--r--lib/hipe/regalloc/hipe_optimistic_regalloc.erl4
-rw-r--r--lib/hipe/rtl/hipe_icode2rtl.erl4
-rw-r--r--lib/hipe/rtl/hipe_rtl.erl174
-rw-r--r--lib/hipe/rtl/hipe_rtl.hrl3
-rw-r--r--lib/hipe/rtl/hipe_rtl_liveness.erl3
-rw-r--r--lib/hipe/rtl/hipe_tagscheme.erl37
-rw-r--r--lib/hipe/sparc/hipe_sparc_assemble.erl48
-rw-r--r--lib/hipe/test/Makefile7
-rw-r--r--lib/hipe/test/hipe_testsuite_driver.erl25
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_build_and_match_aliasing.erl20
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_build_and_match_empty_val.erl17
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_build_and_match_literals.erl40
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_build_and_match_over_alloc.erl16
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_build_and_match_val.erl23
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_expand_map_update.erl7
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_export.erl11
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_get_map_elements.erl23
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_guard_bifs.erl31
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_guard_fun.erl36
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_guard_receive.erl54
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_guard_sequence.erl35
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_guard_update.erl14
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_has_map_fields.erl46
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_is_map.erl24
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_list_comprehension.erl6
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_map_size.erl29
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_map_sort_literals.erl41
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_match_and_update_literals.erl24
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_put_map_assoc.erl23
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_put_map_exact.erl28
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_update_assoc.erl22
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_update_exact.erl32
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_update_literals.erl13
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_update_map_expressions.erl32
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_update_values.erl28
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_warn_pair_key_overloaded.erl27
-rw-r--r--lib/hipe/test/maps_SUITE_data/maps_warn_useless_build.erl9
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/hipe/x86/hipe_x86_assemble.erl51
-rw-r--r--lib/ic/doc/src/notes.xml18
-rw-r--r--lib/ic/src/ic.app.src3
-rw-r--r--lib/ic/src/ic.erl2
-rw-r--r--lib/ic/src/ic_codegen.erl3
-rw-r--r--lib/ic/src/ic_pp.erl8
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE.erl2
-rw-r--r--lib/ic/vsn.mk2
-rw-r--r--lib/inets/doc/src/notes.xml125
-rw-r--r--lib/inets/src/http_client/httpc_cookie.erl3
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl83
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl6
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl46
-rw-r--r--lib/inets/src/http_lib/http_internal.hrl4
-rw-r--r--lib/inets/src/http_server/httpd.erl12
-rw-r--r--lib/inets/src/http_server/httpd_example.erl4
-rw-r--r--lib/inets/src/http_server/httpd_manager.erl36
-rw-r--r--lib/inets/src/http_server/httpd_request.erl204
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl31
-rw-r--r--lib/inets/src/http_server/httpd_util.erl9
-rw-r--r--lib/inets/src/inets_app/inets.app.src4
-rw-r--r--lib/inets/src/inets_app/inets.appup.src13
-rw-r--r--lib/inets/test/Makefile2
-rw-r--r--lib/inets/test/http_format_SUITE.erl15
-rw-r--r--lib/inets/test/httpc_SUITE.erl112
-rw-r--r--lib/inets/test/httpd_1_0.erl9
-rw-r--r--lib/inets/test/httpd_1_1.erl111
-rw-r--r--lib/inets/test/httpd_SUITE.erl1490
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl322
-rw-r--r--lib/inets/test/httpd_block.erl6
-rw-r--r--lib/inets/test/httpd_test_lib.erl121
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl145
-rw-r--r--lib/inets/test/inets_sup_SUITE_data/mime.types3
-rw-r--r--lib/inets/test/inets_sup_SUITE_data/simple.conf6
-rw-r--r--lib/inets/test/old_httpd_SUITE.erl128
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/doc/src/notes.xml19
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java5
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java4
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/Maps.java8
-rw-r--r--lib/jinterface/vsn.mk2
-rw-r--r--lib/kernel/doc/src/app.xml36
-rw-r--r--lib/kernel/doc/src/application.xml5
-rw-r--r--lib/kernel/doc/src/file.xml14
-rw-r--r--lib/kernel/doc/src/inet.xml2
-rw-r--r--lib/kernel/doc/src/kernel_app.xml43
-rw-r--r--lib/kernel/doc/src/notes.xml197
-rw-r--r--lib/kernel/include/dist.hrl3
-rw-r--r--lib/kernel/src/application.erl5
-rw-r--r--lib/kernel/src/application_controller.erl31
-rw-r--r--lib/kernel/src/code_server.erl18
-rw-r--r--lib/kernel/src/disk_log_server.erl12
-rw-r--r--lib/kernel/src/dist_util.erl5
-rw-r--r--lib/kernel/src/erts_debug.erl21
-rw-r--r--lib/kernel/src/file.erl3
-rw-r--r--lib/kernel/src/global.erl22
-rw-r--r--lib/kernel/src/hipe_unified_loader.erl35
-rw-r--r--lib/kernel/src/inet_config.erl3
-rw-r--r--lib/kernel/src/kernel.app.src3
-rw-r--r--lib/kernel/src/os.erl13
-rw-r--r--lib/kernel/test/application_SUITE.erl31
-rw-r--r--lib/kernel/test/application_SUITE_data/t4.config1
-rw-r--r--lib/kernel/test/code_SUITE.erl74
-rw-r--r--lib/kernel/test/code_SUITE_data/calendar.erl23
-rw-r--r--lib/kernel/test/file_SUITE.erl99
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl33
-rw-r--r--lib/kernel/test/heart_SUITE.erl8
-rw-r--r--lib/kernel/test/kernel_SUITE.erl12
-rw-r--r--lib/kernel/test/prim_file_SUITE.erl119
-rw-r--r--lib/kernel/test/sendfile_SUITE.erl7
-rw-r--r--lib/megaco/aclocal.m410
-rw-r--r--lib/megaco/doc/src/notes.xml18
-rw-r--r--lib/megaco/src/app/megaco.app.src5
-rw-r--r--lib/megaco/vsn.mk2
-rw-r--r--lib/mnesia/doc/src/notes.xml64
-rw-r--r--lib/mnesia/src/mnesia.app.src3
-rw-r--r--lib/mnesia/src/mnesia.hrl4
-rw-r--r--lib/mnesia/src/mnesia_controller.erl9
-rw-r--r--lib/mnesia/src/mnesia_index.erl6
-rw-r--r--lib/mnesia/src/mnesia_lib.erl13
-rw-r--r--lib/mnesia/src/mnesia_locker.erl12
-rw-r--r--lib/mnesia/src/mnesia_recover.erl6
-rw-r--r--lib/mnesia/src/mnesia_snmp_hook.erl8
-rw-r--r--lib/mnesia/test/mnesia_config_test.erl7
-rw-r--r--lib/mnesia/test/mnesia_evil_coverage_test.erl11
-rw-r--r--lib/mnesia/test/mnesia_qlc_test.erl2
-rw-r--r--lib/mnesia/test/mnesia_test_lib.hrl35
-rw-r--r--lib/mnesia/test/mnesia_trans_access_test.erl7
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/doc/src/notes.xml62
-rw-r--r--lib/observer/src/cdv_dist_cb.erl12
-rw-r--r--lib/observer/src/cdv_timer_cb.erl7
-rw-r--r--lib/observer/src/cdv_virtual_list_wx.erl11
-rw-r--r--lib/observer/src/crashdump_viewer.erl54
-rw-r--r--lib/observer/src/crashdump_viewer.hrl1
-rw-r--r--lib/observer/src/observer.app.src5
-rw-r--r--lib/observer/src/observer.erl7
-rw-r--r--lib/observer/src/observer_lib.erl2
-rw-r--r--lib/observer/src/observer_procinfo.erl5
-rw-r--r--lib/observer/src/observer_tv_table.erl4
-rw-r--r--lib/observer/src/observer_tv_wx.erl6
-rw-r--r--lib/observer/src/observer_wx.erl17
-rw-r--r--lib/observer/test/crashdump_helper.erl4
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl109
-rw-r--r--lib/observer/test/observer_SUITE.erl248
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/aclocal.m410
-rw-r--r--lib/odbc/doc/src/notes.xml52
-rw-r--r--lib/odbc/src/odbc.app.src3
-rw-r--r--lib/odbc/vsn.mk2
-rw-r--r--lib/orber/doc/src/notes.xml21
-rw-r--r--lib/orber/src/orber.app.src4
-rw-r--r--lib/orber/vsn.mk2
-rw-r--r--lib/os_mon/c_src/memsup.c8
-rw-r--r--lib/os_mon/doc/src/notes.xml42
-rw-r--r--lib/os_mon/src/os_mon.app.src5
-rw-r--r--lib/os_mon/test/cpu_sup_SUITE.erl6
-rw-r--r--lib/os_mon/test/disksup_SUITE.erl17
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/ose/doc/src/ose_intro.xml4
-rw-r--r--lib/ose/src/ose.app.src3
-rw-r--r--lib/otp_mibs/doc/src/notes.xml49
-rw-r--r--lib/otp_mibs/src/otp_mibs.app.src4
-rw-r--r--lib/otp_mibs/vsn.mk2
-rw-r--r--lib/parsetools/doc/src/notes.xml36
-rw-r--r--lib/parsetools/src/parsetools.app.src3
-rw-r--r--lib/parsetools/vsn.mk2
-rw-r--r--lib/percept/doc/src/notes.xml29
-rw-r--r--lib/percept/src/percept.app.src4
-rw-r--r--lib/percept/vsn.mk2
-rw-r--r--lib/public_key/doc/src/cert_records.xml4
-rw-r--r--lib/public_key/doc/src/notes.xml64
-rw-r--r--lib/public_key/doc/src/part.xml4
-rw-r--r--lib/public_key/doc/src/public_key.xml41
-rw-r--r--lib/public_key/src/pubkey_cert.erl17
-rw-r--r--lib/public_key/src/pubkey_crl.erl36
-rw-r--r--lib/public_key/src/public_key.app.src4
-rw-r--r--lib/public_key/vsn.mk2
-rw-r--r--lib/reltool/doc/src/notes.xml48
-rw-r--r--lib/reltool/src/reltool.app.src4
-rw-r--r--lib/reltool/src/reltool.hrl3
-rw-r--r--lib/reltool/src/reltool_server.erl3
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl9
-rw-r--r--lib/reltool/test/reltool_test_lib.erl7
-rw-r--r--lib/reltool/vsn.mk2
-rw-r--r--lib/runtime_tools/doc/specs/.gitignore1
-rw-r--r--lib/runtime_tools/doc/src/Makefile10
-rw-r--r--lib/runtime_tools/doc/src/notes.xml89
-rw-r--r--lib/runtime_tools/doc/src/ref_man.xml1
-rw-r--r--lib/runtime_tools/doc/src/specs.xml4
-rw-r--r--lib/runtime_tools/doc/src/system_information.xml98
-rw-r--r--lib/runtime_tools/src/runtime_tools.app.src4
-rw-r--r--lib/runtime_tools/src/system_information.erl282
-rw-r--r--lib/runtime_tools/test/system_information_SUITE.erl10
-rw-r--r--lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat3
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/doc/src/appup.xml23
-rw-r--r--lib/sasl/doc/src/notes.xml58
-rw-r--r--lib/sasl/examples/src/target_system.erl16
-rw-r--r--lib/sasl/src/sasl.app.src4
-rw-r--r--lib/sasl/src/systools_make.erl2
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl66
-rw-r--r--lib/sasl/test/sasl_SUITE.erl12
-rw-r--r--lib/sasl/test/systools_SUITE.erl58
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/ebin/app1.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/src/myapp.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/ebin/app2.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/src/myapp.erl2
-rw-r--r--lib/snmp/doc/src/notes.xml31
-rw-r--r--lib/snmp/doc/src/snmpa_mib_data.xml4
-rw-r--r--lib/snmp/doc/src/snmpa_mib_storage.xml4
-rw-r--r--lib/snmp/src/app/snmp.app.src4
-rw-r--r--lib/snmp/test/Makefile4
-rw-r--r--lib/snmp/test/modules.mk3
-rw-r--r--lib/snmp/test/snmp_agent_test.erl300
-rw-r--r--lib/snmp/test/snmp_agent_test_lib.erl22
-rw-r--r--lib/snmp/test/snmp_manager_test.erl337
-rw-r--r--lib/snmp/test/snmp_test_mgr.erl3
-rw-r--r--lib/snmp/test/snmp_test_mgr_counter_server.erl152
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/src/notes.xml172
-rw-r--r--lib/ssh/doc/src/ssh.xml30
-rw-r--r--lib/ssh/doc/src/using_ssh.xml4
-rw-r--r--lib/ssh/src/ssh.app.src4
-rw-r--r--lib/ssh/src/ssh.appup.src4
-rw-r--r--lib/ssh/src/ssh.erl21
-rw-r--r--lib/ssh/src/ssh_acceptor.erl44
-rw-r--r--lib/ssh/src/ssh_auth.erl4
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl17
-rw-r--r--lib/ssh/src/ssh_io.erl2
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl140
-rw-r--r--lib/ssh/test/ssh_unicode_SUITE.erl5
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml159
-rw-r--r--lib/ssl/doc/src/ssl.xml97
-rw-r--r--lib/ssl/internal_doc/ssl-implementation.txt52
-rw-r--r--lib/ssl/src/Makefile3
-rw-r--r--lib/ssl/src/dtls_connection.erl725
-rw-r--r--lib/ssl/src/dtls_connection.hrl21
-rw-r--r--lib/ssl/src/dtls_connection_sup.erl12
-rw-r--r--lib/ssl/src/dtls_handshake.erl402
-rw-r--r--lib/ssl/src/dtls_handshake.hrl4
-rw-r--r--lib/ssl/src/dtls_record.erl57
-rw-r--r--lib/ssl/src/dtls_record.hrl13
-rw-r--r--lib/ssl/src/dtls_v1.erl4
-rw-r--r--lib/ssl/src/ssl.app.src5
-rw-r--r--lib/ssl/src/ssl.appup.src24
-rw-r--r--lib/ssl/src/ssl.erl390
-rw-r--r--lib/ssl/src/ssl_alert.erl47
-rw-r--r--lib/ssl/src/ssl_alert.hrl18
-rw-r--r--lib/ssl/src/ssl_api.hrl33
-rw-r--r--lib/ssl/src/ssl_cipher.erl40
-rw-r--r--lib/ssl/src/ssl_cipher.hrl12
-rw-r--r--lib/ssl/src/ssl_connection.erl222
-rw-r--r--lib/ssl/src/ssl_connection.hrl11
-rw-r--r--lib/ssl/src/ssl_dist_sup.erl16
-rw-r--r--lib/ssl/src/ssl_handshake.erl138
-rw-r--r--lib/ssl/src/ssl_handshake.hrl16
-rw-r--r--lib/ssl/src/ssl_internal.hrl22
-rw-r--r--lib/ssl/src/ssl_listen_tracker_sup.erl71
-rw-r--r--lib/ssl/src/ssl_manager.erl42
-rw-r--r--lib/ssl/src/ssl_record.erl19
-rw-r--r--lib/ssl/src/ssl_record.hrl6
-rw-r--r--lib/ssl/src/ssl_socket.erl209
-rw-r--r--lib/ssl/src/ssl_sup.erl19
-rw-r--r--lib/ssl/src/ssl_v3.erl4
-rw-r--r--lib/ssl/src/tls_connection.erl146
-rw-r--r--lib/ssl/src/tls_connection_sup.erl4
-rw-r--r--lib/ssl/src/tls_handshake.erl16
-rw-r--r--lib/ssl/src/tls_handshake.hrl4
-rw-r--r--lib/ssl/src/tls_record.erl40
-rw-r--r--lib/ssl/src/tls_v1.erl64
-rw-r--r--lib/ssl/test/Makefile1
-rw-r--r--lib/ssl/test/make_certs.erl315
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl329
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl542
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl26
-rw-r--r--lib/ssl/test/ssl_test_lib.erl246
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl2
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/Makefile1
-rw-r--r--lib/stdlib/doc/src/epp.xml64
-rw-r--r--lib/stdlib/doc/src/erl_parse.xml10
-rw-r--r--lib/stdlib/doc/src/erl_tar.xml27
-rw-r--r--lib/stdlib/doc/src/maps.xml338
-rw-r--r--lib/stdlib/doc/src/notes.xml384
-rw-r--r--lib/stdlib/doc/src/pg.xml5
-rw-r--r--lib/stdlib/doc/src/ref_man.xml1
-rw-r--r--lib/stdlib/doc/src/specs.xml1
-rw-r--r--lib/stdlib/doc/src/supervisor.xml6
-rw-r--r--lib/stdlib/doc/src/sys.xml73
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml4
-rw-r--r--lib/stdlib/doc/src/zip.xml13
-rw-r--r--lib/stdlib/examples/erl_id_trans.erl2
-rw-r--r--lib/stdlib/src/dets.erl12
-rw-r--r--lib/stdlib/src/dict.erl3
-rw-r--r--lib/stdlib/src/epp.erl177
-rw-r--r--lib/stdlib/src/erl_compile.erl18
-rw-r--r--lib/stdlib/src/erl_eval.erl29
-rw-r--r--lib/stdlib/src/erl_expand_records.erl6
-rw-r--r--lib/stdlib/src/erl_lint.erl354
-rw-r--r--lib/stdlib/src/erl_parse.yrl85
-rw-r--r--lib/stdlib/src/erl_pp.erl13
-rw-r--r--lib/stdlib/src/erl_tar.erl38
-rw-r--r--lib/stdlib/src/escript.erl15
-rw-r--r--lib/stdlib/src/filelib.erl2
-rw-r--r--lib/stdlib/src/gen.erl6
-rw-r--r--lib/stdlib/src/gen_event.erl37
-rw-r--r--lib/stdlib/src/gen_fsm.erl20
-rw-r--r--lib/stdlib/src/gen_server.erl16
-rw-r--r--lib/stdlib/src/io.erl10
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl2
-rw-r--r--lib/stdlib/src/maps.erl35
-rw-r--r--lib/stdlib/src/otp_internal.erl51
-rw-r--r--lib/stdlib/src/pg.erl3
-rw-r--r--lib/stdlib/src/qlc_pt.erl11
-rw-r--r--lib/stdlib/src/sets.erl5
-rw-r--r--lib/stdlib/src/slave.erl23
-rw-r--r--lib/stdlib/src/stdlib.app.src5
-rw-r--r--lib/stdlib/src/sys.erl65
-rw-r--r--lib/stdlib/src/zip.erl37
-rw-r--r--lib/stdlib/test/Makefile5
-rw-r--r--lib/stdlib/test/binary_module_SUITE.erl4
-rw-r--r--lib/stdlib/test/dets_SUITE.erl116
-rw-r--r--lib/stdlib/test/epp_SUITE.erl78
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl9
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl265
-rw-r--r--lib/stdlib/test/erl_lint_SUITE_data/predef.erl4
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl10
-rw-r--r--lib/stdlib/test/erl_scan_SUITE.erl26
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl40
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl11
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl20
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl15
-rw-r--r--lib/stdlib/test/maps_SUITE.erl69
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl19
-rw-r--r--lib/stdlib/test/shell_SUITE.erl2
-rw-r--r--lib/stdlib/test/stdlib_SUITE.erl12
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl57
-rw-r--r--lib/stdlib/test/sys_SUITE.erl84
-rw-r--r--lib/stdlib/test/sys_sp1.erl114
-rw-r--r--lib/stdlib/test/sys_sp2.erl107
-rw-r--r--lib/stdlib/test/tar_SUITE.erl66
-rw-r--r--lib/stdlib/test/unicode_SUITE.erl12
-rw-r--r--lib/syntax_tools/doc/src/notes.xml50
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl4
-rw-r--r--lib/syntax_tools/src/syntax_tools.app.src3
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE.erl14
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/test_server/doc/src/notes.xml42
-rw-r--r--lib/test_server/doc/src/test_server_ctrl.xml11
-rw-r--r--lib/test_server/src/test_server.app.src5
-rw-r--r--lib/test_server/src/test_server.erl14
-rw-r--r--lib/test_server/src/test_server_ctrl.erl302
-rw-r--r--lib/test_server/src/test_server_node.erl10
-rw-r--r--lib/test_server/src/test_server_sup.erl4
-rw-r--r--lib/test_server/src/ts.erl12
-rw-r--r--lib/test_server/src/ts.unix.config2
-rw-r--r--lib/test_server/src/ts_run.erl5
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl16
-rw-r--r--lib/test_server/vsn.mk2
-rw-r--r--lib/tools/doc/src/notes.xml76
-rw-r--r--lib/tools/emacs/erlang.el20
-rw-r--r--lib/tools/emacs/test.erl.indented11
-rw-r--r--lib/tools/emacs/test.erl.orig11
-rw-r--r--lib/tools/src/cover.erl19
-rw-r--r--lib/tools/src/tools.app.src22
-rw-r--r--lib/tools/test/cover_SUITE.erl36
-rw-r--r--lib/tools/test/cover_SUITE_data/f.erl7
-rw-r--r--lib/tools/test/eprof_SUITE.erl10
-rw-r--r--lib/tools/test/xref_SUITE.erl2
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/typer/Makefile2
-rw-r--r--lib/typer/doc/Makefile39
-rw-r--r--lib/typer/doc/html/.gitignore0
-rw-r--r--lib/typer/doc/pdf/.gitignore0
-rw-r--r--lib/typer/doc/src/Makefile117
-rw-r--r--lib/typer/doc/src/book.xml41
-rw-r--r--lib/typer/doc/src/fascicules.xml12
-rw-r--r--lib/typer/doc/src/notes.xml51
-rw-r--r--lib/typer/doc/src/part_notes.xml35
-rw-r--r--lib/typer/doc/src/ref_man.xml35
-rw-r--r--lib/typer/doc/src/typer_app.xml43
-rw-r--r--lib/typer/info2
-rw-r--r--lib/typer/src/Makefile2
-rw-r--r--lib/typer/src/typer.app.src4
-rw-r--r--lib/typer/vsn.mk2
-rw-r--r--lib/webtool/doc/src/notes.xml29
-rw-r--r--lib/webtool/src/webtool.app.src4
-rw-r--r--lib/webtool/vsn.mk2
-rw-r--r--lib/wx/aclocal.m410
-rw-r--r--lib/wx/api_gen/wx_doxygen.conf3
-rw-r--r--lib/wx/api_gen/wx_extra/wxListCtrl.c_src4
-rw-r--r--lib/wx/api_gen/wx_gen.erl12
-rw-r--r--lib/wx/api_gen/wx_gen_cpp.erl19
-rw-r--r--lib/wx/api_gen/wx_gen_erl.erl26
-rw-r--r--lib/wx/api_gen/wxapi.conf19
-rw-r--r--lib/wx/c_src/gen/wxe_derived_dest.h8
-rw-r--r--lib/wx/c_src/gen/wxe_events.cpp142
-rw-r--r--lib/wx/c_src/gen/wxe_funcs.cpp250
-rw-r--r--lib/wx/c_src/gen/wxe_macros.h1217
-rw-r--r--lib/wx/c_src/wxe_callback_impl.cpp2
-rw-r--r--lib/wx/c_src/wxe_callback_impl.h9
-rw-r--r--lib/wx/c_src/wxe_impl.cpp32
-rw-r--r--lib/wx/c_src/wxe_impl.h17
-rwxr-xr-xlib/wx/configure.in14
-rw-r--r--lib/wx/doc/src/notes.xml53
-rw-r--r--lib/wx/include/wx.hrl245
-rw-r--r--lib/wx/src/gen/wxLocale.erl278
-rw-r--r--lib/wx/src/gen/wx_misc.erl18
-rw-r--r--lib/wx/src/gen/wxe_debug.hrl1217
-rw-r--r--lib/wx/src/gen/wxe_funcs.hrl1217
-rw-r--r--lib/wx/src/wx.app.src3
-rw-r--r--lib/wx/src/wxe_master.erl18
-rw-r--r--lib/wx/src/wxe_server.erl13
-rw-r--r--lib/wx/test/wx_basic_SUITE.erl13
-rw-r--r--lib/wx/test/wx_class_SUITE.erl17
-rw-r--r--lib/wx/vsn.mk2
-rw-r--r--lib/xmerl/doc/src/notes.xml29
-rw-r--r--lib/xmerl/src/xmerl.app.src3
-rw-r--r--lib/xmerl/vsn.mk2
-rw-r--r--make/ose_lm.mk.in4
-rw-r--r--make/otp_released_app.mk43
-rw-r--r--make/otp_subdir.mk9
-rwxr-xr-xmake/verify_runtime_dependencies313
-rwxr-xr-xotp_build129
-rw-r--r--otp_versions.table3
-rw-r--r--system/doc/design_principles/applications.xml7
-rw-r--r--system/doc/design_principles/release_handling.xml17
-rw-r--r--system/doc/design_principles/spec_proc.xml65
-rw-r--r--system/doc/getting_started/seq_prog.xml123
-rw-r--r--system/doc/installation_guide/Makefile8
-rw-r--r--system/doc/installation_guide/install-binary.xml127
-rw-r--r--system/doc/installation_guide/otp_version.xml96
-rw-r--r--system/doc/installation_guide/part.xml5
-rw-r--r--system/doc/installation_guide/verification.xml102
-rw-r--r--system/doc/installation_guide/xmlfiles.mk7
-rw-r--r--system/doc/reference_manual/character_set.xml132
-rw-r--r--system/doc/reference_manual/data_types.xml32
-rw-r--r--system/doc/reference_manual/expressions.xml246
-rw-r--r--system/doc/reference_manual/introduction.xml85
-rw-r--r--system/doc/reference_manual/modules.xml27
-rw-r--r--system/doc/reference_manual/part.xml3
-rw-r--r--system/doc/reference_manual/typespec.xml12
-rw-r--r--system/doc/system_principles/create_target.xmlsrc248
-rw-r--r--system/doc/system_principles/part.xml5
-rw-r--r--system/doc/system_principles/system_principles.xml9
-rw-r--r--system/doc/system_principles/upgrade.xml118
-rw-r--r--system/doc/system_principles/versions.xml267
-rw-r--r--system/doc/system_principles/xmlfiles.mk6
-rw-r--r--xcomp/erl-xcomp-arm-android.conf264
-rw-r--r--xcomp/erl-xcomp-powerpc-ose5.conf10
-rw-r--r--xcomp/erl-xcomp-vars.sh2
913 files changed, 36741 insertions, 10922 deletions
diff --git a/.gitignore b/.gitignore
index b391ecbe57..d40f49b56f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -98,6 +98,8 @@ lib/wx/c_src/win32/
lib/wx/priv/win32/
lib/wx/win32/
make/win32/
+make/otp_built
+make/otp_doc_built
# OSE
*.d
diff --git a/HOWTO/INSTALL-ANDROID.md b/HOWTO/INSTALL-ANDROID.md
new file mode 100644
index 0000000000..31698d4ce3
--- /dev/null
+++ b/HOWTO/INSTALL-ANDROID.md
@@ -0,0 +1,52 @@
+Cross Compiling Erlang/OTP - ANDROID
+====================================
+
+Introduction
+------------
+
+This document describes how to cross compile Erlang OTP to Android/Rasberry Pi platforms.
+
+### Download and Install Android NDK ###
+
+https://developer.android.com/tools/sdk/ndk/index.html
+
+### Define System Variables ###
+
+export NDK_ROOT=/usr/local/android
+export NDK_PLAT=android-9
+export PATH=$NDK_ROOT/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin:$PATH
+
+### Configure OTP ###
+
+./otp_build configure \
+ --xcomp-conf=./xcomp/erl-xcomp-arm-android.conf \
+ --without-ssl
+
+### Compile OTP ###
+
+make noboot [-j4]
+
+### Make Release ###
+
+./otp_build release -a /usr/local/otp_R16B03_arm
+
+### Target Deployment ###
+
+Make a tarball out of /usr/local/otp_R16B03_arm and copy it to target device
+(e.g. Raspberry Pi). Extract it and install
+
+./Install /usr/local/otp_R16B03_arm
+
+Android SDK (adb tool) is used to deploy OTP/Erlang to target device for
+evaluation purpose only.
+
+adb push /usr/local/otp_R16B03_arm /mnt/sdcard/otp_R16B03_arm
+adb shell
+
+### Known Issues ###
+
+ * native inet:gethostbyname/1 return {error, nxdomain} on Raspberry PI. Use dns resolver to by-pass the issue (see http://www.erlang.org/doc/apps/erts/inet_cfg.html)
+
+### References ###
+
+ The port derives some solutions from https://code.google.com/p/erlang4android/
diff --git a/HOWTO/INSTALL-CROSS.md b/HOWTO/INSTALL-CROSS.md
index cb9d12e753..af4b38f292 100644
--- a/HOWTO/INSTALL-CROSS.md
+++ b/HOWTO/INSTALL-CROSS.md
@@ -537,7 +537,11 @@ When a variable has been set, no warning will be issued.
* `erl_xcomp_ose_LM_LCF` - OSE load module linker configuration file
-* `erl_xcomp_ose_LM_CONF` - OSE load module default configuration file
+* `erl_xcomp_ose_BEAM_LM_CONF` - Beam OSE load module configuration file
+
+* `erl_xcomp_ose_EPMD_LM_CONF` - EPMD OSE load module configuration file
+
+* `erl_xcomp_ose_RUN_ERL_LM_CONF` - run_erl_lm OSE load module configuration file
Copyright and License
---------------------
@@ -559,12 +563,6 @@ under the License.
%CopyrightEnd%
-Modifying This Document
------------------------
-
-Before modifying this document you need to have a look at the
-[$ERL_TOP/HOWTO/MARKDOWN.md][] document.
-
[$ERL_TOP/HOWTO/INSTALL.md]: INSTALL.md
@@ -572,6 +570,4 @@ Before modifying this document you need to have a look at the
[How to Build the Documentation]: INSTALL.md#The-ErlangOTP-Documentation_How-to-Build-the-Documentation
[cross configuration variables]: #Currently-Used-Configuration-Variables
[DESTDIR]: http://www.gnu.org/prep/standards/html_node/DESTDIR.html
- [$ERL_TOP/HOWTO/MARKDOWN.md]: MARKDOWN.md
-
[?TOC]: true
diff --git a/HOWTO/INSTALL-WIN32.md b/HOWTO/INSTALL-WIN32.md
index 94d3688f23..0387572dd3 100644
--- a/HOWTO/INSTALL-WIN32.md
+++ b/HOWTO/INSTALL-WIN32.md
@@ -1018,7 +1018,7 @@ Copyright and License
%CopyrightBegin%
-Copyright Ericsson AB 2003-2012. All Rights Reserved.
+Copyright Ericsson AB 2003-2014. All Rights Reserved.
The contents of this file are subject to the Erlang Public License,
Version 1.1, (the "License"); you may not use this file except in
@@ -1033,15 +1033,7 @@ under the License.
%CopyrightEnd%
-Modifying This Document
------------------------
-
-Before modifying this document you need to have a look at the
-[$ERL_TOP/HOWTO/MARKDOWN.md][] document.
-
-
[1]: http://www.erlang.org/faq.html "mailing lists"
- [$ERL_TOP/HOWTO/MARKDOWN.md]: MARKDOWN.md
[?TOC]: true
diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md
index bbde5bc08c..5b3a09df2b 100644
--- a/HOWTO/INSTALL.md
+++ b/HOWTO/INSTALL.md
@@ -4,174 +4,109 @@ Building and Installing Erlang/OTP
Introduction
------------
-This document describes how to build and install Erlang/OTP-%OTP-REL%. You
-are advised to read the whole document before attempting to build and install
-Erlang/OTP. You can find more information about Open Source Erlang/OTP at:
+This document describes how to build and install Erlang/OTP-%OTP-REL%.
+Erlang/OTP should be possible to build from source on any Unix/Linux system,
+including OS X. You are advised to read the whole document
+before attempting to build and install Erlang/OTP.
- <http://www.erlang.org/>
+The source code can be downloaded from the official site of Erlang/OTP or GitHub.
+* <http://www.erlang.org>
+* <https://github.com/erlang/otp>
-The source code for Erlang/OTP can also be found in a Git repository:
-
- <http://github.com/erlang/otp>
-
-Erlang/OTP should be possible to build from source on any Unix system,
-including Mac OS X. This document describes how to native compile Erlang/OTP
-on Unix. For detailed instructions on how to
-
-* cross compile Erlang/OTP, see the [$ERL_TOP/HOWTO/INSTALL-CROSS.md][]
- document.
-
-* build Erlang/OTP on Windows, see the [$ERL_TOP/HOWTO/INSTALL-WIN32.md][]
- document.
+Required Utilities
+------------------
- Binary releases for Windows can be found at
- <http://www.erlang.org/download.html>.
+These are the tools you need in order to unpack and build Erlang/OTP.
-Before reading the above mentioned documents you are in any case advised to
-read this document first, since it covers building Erlang/OTP in general as
-well as other important information.
+> *WARNING*: Please have a look at the [Known platform issues][] chapter
+> before you start.
-Daily Build and Test
---------------------
-At Ericsson we have a "Daily Build and Test" that runs on:
-
-* Solaris 8, 9
- * Sparc32
- * Sparc64
-* Solaris 10
- * Sparc32
- * Sparc64
- * x86
-* SuSE Linux/GNU 9.4, 10.1
- * x86
-* SuSE Linux/GNU 10.0, 10.1, 11.0
- * x86
- * x86\_64
-* openSuSE 11.4 (Celadon)
- * x86\_64 (valgrind)
-* Fedora 7
- * PowerPC
-* Fedora 14
- * x86\_64
-* Gentoo Linux/GNU 1.12.11.1
- * x86
-* Ubuntu Linux/GNU 7.04, 10.04, 10.10, 11.0
- * x86\_64
-* MontaVista Linux/GNU 4.0.1
- * PowerPC
-* FreeBSD 8.2
- * x86
-* OpenBSD 5.0
- * x86\_64
-* Mac OS X 10.5.8 (Leopard), 10.7.3 (Lion), 10.9 (Mavericks)
- * x86
-* Windows XP SP3, 2003, Vista, 7
- * x86
-* Windows 7
- * x86\_64
+### Unpacking ###
-We also have the following "Daily Cross Builds":
+* GNU unzip, or a modern uncompress.
+* A TAR program that understands the GNU TAR format for long filenames.
-* SuSE Linux/GNU 10.1 x86 -> SuSE Linux/GNU 10.1 x86\_64
-* SuSE Linux/GNU 10.1 x86\_64 -> Linux/GNU TILEPro64
+### Building ###
-and the following "Daily Cross Build Tests":
+* GNU `make`
+* Compiler -- GNU C Compiler, `gcc` or the C compiler frontend for LLVM, `clang`.
+* Perl 5
+* GNU `m4` -- If HiPE (native code) support is enabled. HiPE can be
+ disabled using `--disable-hipe`
+* `ncurses`, `termcap`, or `termlib` -- The development headers and
+ libraries are needed, often known as `ncurses-devel`. Use
+ `--without-termcap` to build without any of these libraries. Note that
+ in this case only the old shell (without any line editing) can be used.
+* `sed` -- Stream Editor for basic text transformation.
-* SuSE Linux/GNU 10.1 x86\_64
+#### Building in Git ####
-Versions Known NOT to Work
---------------------------
+* GNU `autoconf` of at least version 2.59. Note that `autoconf` is not
+ needed when building an unmodified version of the released source.
-* Suse linux 9.1 is shipped with a patched GCC version 3.3.3, having the
- rpm named `gcc-3.3.3-41`. That version has a serious optimization bug
- that makes it unusable for building the Erlang emulator. Please
- upgrade GCC to a newer version before building on Suse 9.1. Suse Linux
- Enterprise edition 9 (SLES9) has `gcc-3.3.3-43` and is not affected.
+#### Building on OS X ####
-* `gcc-4.3.0` has a serious optimizer bug. It produces an Erlang emulator
- that will crash immediately. The bug is supposed to be fixed in
- `gcc-4.3.1`.
+* Xcode -- Download and install via the Mac App Store.
+ Read about [Building on a Mac][] before proceeding.
-* FreeBSD had a bug which caused `kqueue`/`poll`/`select` to fail to detect
- that a `writev()` on a pipe has been made. This bug should have been fixed
- in FreeBSD 6.3 and FreeBSD 7.0. NetBSD and DragonFlyBSD probably have or
- have had the same bug. More information can be found at:
+### Installing ###
- * <http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/kern/sys_pipe.c>
- * <http://lists.freebsd.org/pipermail/freebsd-arch/2007-September/006790.html>
+* An `install` program that can take multiple file names.
-* `getcwd()` on Solaris 9 can cause an emulator crash. If you have
- async-threads enabled you can increase the stack size of the
- async-threads as a temporary workaround. See the `+a` command-line
- argument in the documentation of `erl(1)`. Without async-threads the
- emulator is not as vulnerable to this bug, but if you hit it without
- async-threads the only workaround available is to enable async-threads
- and increase the stack size of the async-threads. Sun has however
- released patches that fixes the issue:
- > Problem Description: 6448300 large mnttab can cause stack overrun
- > during Solaris 9 getcwd
+Optional Utilities
+------------------
- More information can be found at:
+Some applications are automatically skipped if the dependencies aren't met.
+Here is a list of utilities needed for those applications. You will
+also find the utilities needed for building the documentation.
- * <http://sunsolve.sun.com/search/document.do?assetkey=1-21-112874-40-1&searchclause=6448300>
- * <http://sunsolve.sun.com/search/document.do?assetkey=1-21-114432-29-1&searchclause=6448300>
+### Building ###
-Required Utilities
-------------------
+* OpenSSL -- The opensource toolkit for Secure Socket Layer
+ and Transport Layer Security.
+ Required for building the application `crypto`.
+ Further, `ssl` and `ssh` require a working crypto application and
+ will also be skipped if OpenSSL is missing. The `public_key`
+ application will available without `crypto`, but the functionality
+ will be very limited.
+
+ The development package of OpenSSL including the header files are needed as well
+ as the binary command program `openssl`. At least version 0.9.8 of OpenSSL is required.
+ Read more and download from <http://www.openssl.org>.
+* Oracle Java SE JDK -- The Java Development Kit (Standard Edition).
+ Required for building the application `jinterface` and parts of `ic` and `orber`.
+ At least version 1.5.0 of the JDK is required.
+
+ Download from <http://www.oracle.com/technetwork/java/javase/downloads>.
+ We have also tested with IBM's JDK 1.5.0.
+* X Windows -- Development headers and libraries are needed
+ to build the Erlang/OTP application `gs` on Unix/Linux.
+* `flex` -- Headers and libraries are needed to build the flex
+ scanner for the `megaco` application on Unix/Linux.
+* wxWidgets -- Toolkit for GUI applications.
+ Required for building the `wx` application. At least
+ version 3.0 of wxWidgets is required.
-These are the tools you will need in order to unpack and build Erlang/OTP.
+ Download from <http://sourceforge.net/projects/wxwindows/files/3.0.0/>
+ or get it from GitHub: <https://github.com/wxWidgets/wxWidgets>
-### Unpacking ###
+ Further instructions on wxWidgets, read [Building with wxErlang][].
-* GNU unzip, or a modern uncompress.
-* A TAR program that understands the GNU TAR format for long filenames
- (such as GNU TAR).
-### Building ###
-* GNU `make`
-* `gcc` -- GNU C compiler
-* Perl 5
-* GNU `m4` -- If HiPE (native code) support is enabled. HiPE can be
- disabled using `--disable-hipe`
-* `ncurses`, `termcap`, or `termlib` -- The development headers and
- libraries are needed, often known as `ncurses-devel`. Use
- `--without-termcap` to build without any of these libraries. Note that
- in this case only the old shell (without any line editing) can be used.
-* OpenSSL -- Optional, but needed for building the Erlang/OTP applications
- `ssl` and `crypto`. You need the "development package" of OpenSSL, i.e.
- including the header files. For building the application `ssl` the OpenSSL
- binary command program `openssl` is also needed. At least version 0.9.8
- of OpenSSL is required. Can be downloaded from <http://www.openssl.org>.
-* Sun Java jdk-1.5.0 or higher -- Optional but needed for building the
- Erlang/OTP application `jinterface` and parts of `ic` and `orber`. Can
- be downloaded from <http://java.sun.com>. We have also tested IBM's
- JDK 1.5.0.
-* X Windows -- Optional, but development headers and libraries are needed
- to build the Erlang/OTP application `gs` on Unix/Linux.
-* `sed` -- There seem to be some problems with some of the `sed` version on
- Solaris. Make sure `/bin/sed` or `/usr/bin/sed` is used on the Solaris
- platform.
-* `flex` -- Optional, headers and libraries are needed to build the `flex`
- scanner for the `megaco` application on Unix/Linux.
+### Building Documentation ###
-#### Building Documentation ####
+* `xsltproc` -- A command line XSLT processor.
-* `xsltproc` -- XSLT processor. A tool for applying XSLT stylesheets
- to XML documents. Can be downloaded from
+ A tool for applying XSLT stylesheets
+ to XML documents. Download xsltproc from
<http://xmlsoft.org/XSLT/xsltproc2.html>.
+
* `fop` -- Apache FOP print formatter (requires Java). Can be downloaded
from <http://xmlgraphics.apache.org/fop>.
-#### Building in Git ####
-* GNU `autoconf` of at least version 2.59. Note that `autoconf` is not
- needed when building an unmodified version of the released source.
-
-### Installing ###
-
-* An `install` program that can take multiple file names.
How to Build and Install Erlang/OTP
-----------------------------------
@@ -186,65 +121,205 @@ section below before proceeding.
### Unpacking ###
-Step 1: Start by unpacking the Erlang/OTP distribution file with your GNU
+Start by unpacking the Erlang/OTP distribution file with your GNU
compatible TAR program.
- $ gunzip -c otp_src_%OTP-VSN%.tar.gz | tar xf -
+ $ tar -zxf otp_src_%OTP-VSN%.tar.gz # Assuming bash/sh
-alternatively:
-
- $ zcat otp_src_%OTP-VSN%.tar.gz | tar xf -
-
-
-Step 2: Now cd into the base directory (`$ERL_TOP`).
+Now change directory into the base directory and set the `$ERL_TOP` variable.
$ cd otp_src_%OTP-VSN%
+ $ export ERL_TOP=`pwd` # Assuming bash/sh
### Configuring ###
-Step 3: On some platforms Perl may behave strangely if certain locales are
-set, so optionally you may need to set the LANG variable:
-
- # Bourne shell
- $ LANG=C; export LANG
+Run the following commands to configure the build:
-or
+ $ ./configure [ options ]
- # C-Shell
- $ setenv LANG C
+> *NOTE*: If you are building Erlang/OTP from git you will need to run `./otp_build autoconf` to generate
+> the configure scripts.
-Step 4: Run the following commands to configure the build:
+By default, Erlang/OTP release will be installed in `/usr/local/{bin,lib/erlang}`.
+If you for instance don't have the permission to install in the standard location,
+ you can install Erlang/OTP somewhere else. For example, to install in
+`/opt/erlang/%OTP-VSN%/{bin,lib/erlang}`, use the `--prefix=/opt/erlang/%OTP-VSN%` option.
- $ ./configure [ options ]
+On some platforms Perl may behave strangely if certain locales are
+set. If you get errors when building, try setting the LANG variable:
-If you are building it from git you will need to run `autoconf` to generate configure file.
-By default, Erlang/OTP will be installed in `/usr/local/{bin,lib/erlang}`.
-To instead install in `<BaseDir>/{bin,lib/erlang}`, use the
-`--prefix=<BaseDir>` option.
+ $ export LANG=C # Assuming bash/sh
-If you upgraded the source with some patch you may need to clean up
-from previous builds before the new build. Before doing a `make clean`,
-be sure to read the [Pre-built Source Release][] section below.
### Building ###
-Step 5: Build the Erlang/OTP package.
+Build the Erlang/OTP release.
$ make
+
+### Testing ###
+
+Before installation you should test whether your build is working properly
+by running our smoke test. The smoke test is a subset of the complete Erlang/OTP test suites.
+First you will need to build and release the test suites.
+
+ $ make release_tests
+
+This creates an additional folder in `$ERL_TOP/release` called `tests`.
+Now, it's time to start the smoke test.
+
+ $ cd release/tests/test_server
+ $ $ERL_TOP/bin/erl -s ts install -s ts smoke_test batch -s init stop
+
+To verify that everything is ok you should open `$ERL_TOP/release/tests/test_server/index.html`
+in your web browser and make sure that there are zero failed test cases.
+
+> *NOTE*: On builds without `crypto`, `ssl` and `ssh` there is a failed test case
+> for undefined functions. Verify that the failed test case log only shows calls
+> to skipped applications.
+
### Installing ###
-Step 6: Install then Erlang/OTP package
+You are now ready to install the Erlang/OTP release!
+The following command will install the release on your system.
$ make install
-### A Closer Look at the individual Steps ###
-Let us go through them in some detail.
+### Running ###
+
+You should now have a working release of Erlang/OTP!
+Jump to [System Principles][] for instructions on running Erlang/OTP.
+
+
+### How to Build the Documentation ###
+
+Make sure you're in the top directory in the source tree.
+
+ $ cd $ERL_TOP
+
+If you have just built Erlang/OTP in the current source tree, you have
+already ran `configure` and do not need to do this again; otherwise, run
+`configure`.
+
+ $ ./configure [Configure Args]
+
+When building the documentation you need a full Erlang/OTP-%OTP-VSN% system in
+the `$PATH`.
+
+ $ export PATH=$ERL_TOP/bin:$PATH # Assuming bash/sh
+
+Build the documentation.
-#### Configuring ####
+ $ make docs
+
+#### Build Issues ####
+
+We have sometimes experienced problems with Oracle's `java` running out of
+memory when running `fop`. Increasing the amount of memory available
+as follows has in our case solved the problem.
+
+ $ export FOP_OPTS="-Xmx<Installed amount of RAM in MB>m"
+
+More information can be found at
+* <http://xmlgraphics.apache.org/fop/0.95/running.html#memory>.
+
+
+### How to Install the Documentation ###
+
+The documentation can be installed either using the `install-docs` target,
+or using the `release_docs` target.
+
+* If you have installed Erlang/OTP using the `install` target, install
+ the documentation using the `install-docs` target. Install locations
+ determined by `configure` will be used. `$DESTDIR` can be used the
+ same way as when doing `make install`.
+
+ $ make install-docs
+
+* If you have installed Erlang/OTP using the `release` target, install
+ the documentation using the `release_docs` target. You typically want
+ to use the same `RELEASE_ROOT` as when invoking `make release`.
+
+ $ make release_docs RELEASE_ROOT=<release dir>
+
+
+### Accessing the Documentation ###
+
+After installation you can access the documentation by
+
+* Reading man pages. Make sure that `erl` is referring to the
+ installed version. For example `/usr/local/bin/erl`.
+ Try viewing at the man page for Mnesia
+
+ $ erl -man mnesia
+
+* Browsing the html pages by loading the page `/usr/local/lib/erlang/doc/erlang/index.html`
+ or `<BaseDir>/lib/erlang/doc/erlang/index.html` if the prefix option has been used.
+
+
+### How to Install the Pre-formatted Documentation ###
+
+Pre-formatted [html documentation][] and [man pages][] can be downloaded from
+* <http://www.erlang.org/download.html>.
+
+Extract the html archive in the installation directory.
+
+ $ cd <ReleaseDir>
+ $ tar -zxf otp_html_%OTP-VSN%.tar.gz
+
+For `erl -man <page>` to work the Unix manual pages have to be
+installed in the same way, i.e.
+
+ $ cd <ReleaseDir>
+ $ tar -zxf otp_man_%OTP-VSN%.tar.gz
+
+Where `<ReleaseDir>` is
+
+* `<PrefixDir>/lib/erlang` if you have installed Erlang/OTP using
+ `make install`.
+* `$DESTDIR<PrefixDir>/lib/erlang` if you have installed Erlang/OTP
+ using `make install DESTDIR=<TmpInstallDir>`.
+* `RELEASE_ROOT` if you have installed using
+ `make release RELEASE_ROOT=<ReleaseDir>`.
+
+
+Advanced configuration and build of Erlang/OTP
+----------------------------------------------
+
+If you want to tailor your Erlang/OTP build and installation, please read
+on for detailed information about the individual steps.
+
+### make and $ERL\_TOP ###
+
+All the makefiles in the entire directory tree use the environment
+variable `ERL_TOP` to find the absolute path of the installation. The
+`configure` script will figure this out and set it in the top level
+Makefile (which, when building, it will pass on). However, when
+developing it is sometimes convenient to be able to run make in a
+subdirectory. To do this you must set the `ERL_TOP` variable
+before you run make.
-Step 4 runs a configuration script created by the GNU autoconf utility, which
+For example, assume your GNU make program is called `make` and you
+want to rebuild the application `STDLIB`, then you could do:
+
+ $ cd lib/stdlib; env ERL_TOP=<Dir> make
+
+where `<Dir>` would be what you find `ERL_TOP` is set to in the top level
+Makefile.
+
+### otp\_build vs configure/make ###
+
+Building Erlang/OTP can be done either by using the `$ERL_TOP/otp_build`
+script, or by invoking `$ERL_TOP/configure` and `make` directly. Building using
+`otp_build` is easier since it involves fewer steps, but the `otp_build` build
+procedure is not as flexible as the `configure`/`make` build procedure. The binary
+releases for Windows that we deliver are built using `otp_build`.
+
+### Configuring ###
+
+The configure script is created by the GNU autoconf utility, which
checks for system specific features and then creates a number of makefiles.
The configure script allows you to customize a number of parameters;
@@ -260,10 +335,10 @@ use the `--prefix` argument like this: `./configure --prefix=<Dir>`.
Some of the available `configure` options are:
* `--prefix=PATH` - Specify installation prefix.
-* `--{enable,disable}-threads` - Thread support (enabled by default if
- possible)
+
+* `--{enable,disable}-threads` - Thread support. This is enabled by default if possible.
* `--{enable,disable}-smp-support` - SMP support (enabled by default if
- possible)
+ a usable POSIX thread library or native Windows threads is found)
* `--{enable,disable}-kernel-poll` - Kernel poll support (enabled by
default if possible)
* `--{enable,disable}-hipe` - HiPE support (enabled by default on supported
@@ -290,7 +365,7 @@ Some of the available `configure` options are:
* `--{with,without}-javac` - Java compiler (without implies that the
`jinterface` application won't be built)
* `--{enable,disable}-dynamic-ssl-lib` - Dynamic OpenSSL libraries
-* `--{enable,disable}-shared-zlib` - Shared zlib library
+* `--{enable,disable}-builtin-zlib` - Use the built-in source for zlib.
* `--with-ssl=PATH` - Specify location of OpenSSL include and lib
* `--{with,without}-ssl` - OpenSSL (without implies that the `crypto`,
`ssh`, and `ssl` won't be built)
@@ -321,9 +396,9 @@ Some of the available `configure` options are:
to configure.
* `--without-$app` - By default all applications in Erlang/OTP will be included
in a release. If this is not wanted it is possible to specify that Erlang/OTP
- should be compiled without that applications, i.e. `--without-wx`. There is
- no automatic dependency handling inbetween applications. So if you disable
- an application that another depends on, you also have to disable the
+ should be compiled without one or more applications, i.e. `--without-wx`. There is
+ no automatic dependency handling between applications. If you disable
+ an application that another application depends on, you also have to disable the
dependant application.
* `--enable-dirty-schedulers` - Enable the **experimental** dirty schedulers
functionality. Note that the dirty schedulers functionality is experimental,
@@ -334,26 +409,138 @@ Some of the available `configure` options are:
If you or your system has special requirements please read the `Makefile` for
additional configuration information.
-#### Building ####
-Step 5 builds the Erlang/OTP system. On a fast computer, this will take about
-5 minutes. After completion of this step, you should have a working
-Erlang/OTP system which you can try by typing `bin/erl`. This should start
-up Erlang/OTP and give you a prompt:
+### Building ###
+
+Building Erlang/OTP on a relatively fast computer takes approximately
+5 minutes. To speed it up, you can utilize parallel make with the `-j<num_jobs>` option.
+
+ $ export MAKEFLAGS=-j8 # Assuming bash/sh
+ $ make
+
+If you've upgraded the source with a patch you may need to clean up from previous
+builds before the new build.
+Make sure to read the [Pre-built Source Release][] section below before doing a `make clean`.
+
+#### Within Git ####
+
+When building in a Git working directory you also have to have a GNU `autoconf`
+of at least version 2.59 on your system, because you need to generate the
+`configure` scripts before you can start building.
+
+The `configure` scripts are generated by invoking `./otp_build autoconf` in
+the `$ERL_TOP` directory. The `configure` scripts also have to be regenerated
+when a `configure.in` or `aclocal.m4` file has been modified. Note that when
+checking out a branch a `configure.in` or `aclocal.m4` file may change
+content, and you may therefore have to regenerate the `configure` scripts
+when checking out a branch. Regenerated `configure` scripts imply that you
+have to run `configure` and build again.
+
+> *NOTE*: Running `./otp_build autoconf` is **not** needed when building
+> an unmodified version of the released source.
+
+Other useful information can be found at our GitHub wiki:
+* <http://wiki.github.com/erlang/otp>
+
+#### OS X (Darwin) ####
+
+Make sure that the command `hostname` returns a valid fully qualified host
+name (this is configured in `/etc/hostconfig`). Otherwise you might experience
+problems when running distributed systems.
+
+If you develop linked-in drivers (shared library) you need to link using
+`gcc` and the flags `-bundle -flat_namespace -undefined suppress`. You also
+include `-fno-common` in `CFLAGS` when compiling. Use `.so` as the library
+suffix.
+
+If you have Xcode 4.3, or later, you will also need to download
+"Command Line Tools" via the Downloads preference pane in Xcode.
+
+#### Building with wxErlang ####
+
+If you want to build the `wx` application, you will need to get wxWidgets-3.0
+(`wxWidgets-3.0.0.tar.bz2` from <http://sourceforge.net/projects/wxwindows/files/3.0.0/>) or get it from github with bug fixes:
+
+ $ git clone --branch WX_3_0_branch [email protected]:wxWidgets/wxWidgets.git
+
+Be aware that the wxWidgets-3.0 is a new release of wxWidgets, it is not as
+mature as the old releases and the OS X port still lags behind the other ports.
+
+Configure and build wxWidgets (on Mavericks - 10.9):
- $ bin/erl
- Erlang/OTP %OTP-REL% [erts-%ERTS-VSN%] [source] [smp:4:4] [async-threads:0] [kernel-poll:false]
+ $ ./configure --with-cocoa --prefix=/usr/local
+ or without support for old versions and with static libs
+ $ ./configure --with-cocoa --prefix=/usr/local --with-macosx-version-min=10.9 --disable-shared
+ $ make
+ $ sudo make install
+ $ export PATH=/usr/local/bin:$PATH
- Eshell V%ERTS-VSN% (abort with ^G)
- 1> _
+Check that you got the correct wx-config
-#### Installing ####
+ $ which wx-config && wx-config --version-full
-Step 6 is optional. It installs Erlang/OTP at a standardized location (if you
-change your mind about where you wish to install you can rerun step 4,
-without having to do step 5 again).
+Build Erlang/OTP
+
+ $ export PATH=/usr/local/bin:$PATH
+ $ cd $ERL_TOP
+ $ ./configure
+ $ make
+ $ sudo make install
-##### Alternative Installation Procedures #####
+
+#### Pre-built Source Release ####
+
+The source release is delivered with a lot of platform independent
+build results already pre-built. If you want to remove these pre-built
+files, invoke `./otp_build remove_prebuilt_files` from the `$ERL_TOP`
+directory. After you have done this, you can build exactly the same way
+as before, but the build process will take a much longer time.
+
+> *WARNING*: Doing `make clean` in an arbitrary directory of the source
+> tree, may remove files needed for bootstrapping the build.
+>
+> Doing `./otp_build save_bootstrap` from the `$ERL_TOP` directory before
+> doing `make clean` will ensure that it will be possible to build after
+> doing `make clean`. `./otp_build save_bootstrap` will be invoked
+> 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.
+
+#### How to Build a Debug Enabled Erlang RunTime System ####
+
+After completing all the normal building steps described above a debug
+enabled runtime system can be built. To do this you have to change
+directory to `$ERL_TOP/erts/emulator`.
+
+In this directory execute:
+
+ $ make debug FLAVOR=$FLAVOR
+
+where `$FLAVOR` is either `plain` or `smp`. The flavor options will
+produce a beam.debug and beam.smp.debug executable respectively. The
+files are installed along side with the normal (opt) versions `beam.smp`
+and `beam`.
+
+To start the debug enabled runtime system execute:
+
+ $ $ERL_TOP/bin/cerl -debug
+
+The debug enabled runtime system features lock violation checking,
+assert checking and various sanity checks to help a developer ensure
+correctness. Some of these features can be enabled on a normal beam
+using appropriate configure options.
+
+There are other types of runtime systems that can be built as well
+using the similar steps just described.
+
+ $ make $TYPE FLAVOR=$FLAVOR
+
+where `$TYPE` is `opt`, `gcov`, `gprof`, `debug`, `valgrind`, or `lcnt`.
+These different beam types are useful for debugging and profiling
+purposes.
+
+
+### Installing ###
* Staged install using [DESTDIR][]. You can perform the install
phase in a temporary directory and later move the installation into
@@ -435,7 +622,7 @@ without having to do step 5 again).
if you want to try the system out, running test suites, etc, before doing
the real install without `EXTRA_PREFIX`.
-### Symbolic Links in --bindir ###
+#### Symbolic Links in --bindir ####
When doing `make install` and the default installation prefix is used,
relative symbolic links will be created from `/usr/local/bin` to all public
@@ -449,156 +636,10 @@ passed to `configure`. One can force relative, or absolute links by passing
phase. Note that such a request might cause a failure if the request cannot
be satisfied.
-### Pre-built Source Release ###
-
-The source release is delivered with a lot of platform independent
-build results already pre-built. If you want to remove these pre-built
-files, invoke `./otp_build remove_prebuilt_files` from the `$ERL_TOP`
-directory. After you have done this, you can build exactly the same way
-as before, but the build process will take a much longer time.
-
-> *WARNING*: Doing `make clean` in an arbitrary directory of the source
-> tree, may remove files needed for bootstrapping the build.
->
-> Doing `./otp_build save_bootstrap` from the `$ERL_TOP` directory before
-> doing `make clean` will ensure that it will be possible to build after
-> doing `make clean`. `./otp_build save_bootstrap` will be invoked
-> 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.
-
-### Building in Git ###
-
-When building in a Git working directory you also have to have a GNU `autoconf`
-of at least version 2.59 on your system, because you need to generate the
-`configure` scripts before you can start building.
-
-The `configure` scripts are generated by invoking `./otp_build autoconf` in
-the `$ERL_TOP` directory. The `configure` scripts also have to be regenerated
-when a `configure.in` or `aclocal.m4` file has been modified. Note that when
-checking out a branch a `configure.in` or `aclocal.m4` file may change
-content, and you may therefore have to regenerate the `configure` scripts
-when checking out a branch. Regenerated `configure` scripts imply that you
-have to run `configure` and build again.
-
-> *NOTE*: Running `./otp_build autoconf` is **not** needed when building
-> an unmodified version of the released source.
-
-Other useful information can be found at our github wiki:
-<http://wiki.github.com/erlang/otp>
-
-### make and $ERL\_TOP ###
-
-All the makefiles in the entire directory tree use the environment
-variable `ERL_TOP` to find the absolute path of the installation. The
-`configure` script will figure this out and set it in the top level
-Makefile (which, when building, it will pass on). However, when
-developing it is sometimes convenient to be able to run make in a
-subdirectory. To do this you must set the `ERL_TOP` variable
-before you run make.
-
-For example, assume your GNU make program is called `make` and you
-want to rebuild the application `STDLIB`, then you could do:
-
- $ cd lib/stdlib; env ERL_TOP=<Dir> make
-
-where `<Dir>` would be what you find `ERL_TOP` is set to in the top level
-Makefile.
-
-The Erlang/OTP Documentation
-----------------------------
-
-### How to Build the Documentation ###
-
-Before you can build the documentation you need to either [native build][]
-or [cross build][] the Erlang/OTP system. After this you can build the
-documentation as follows.
-
- $ cd $ERL_TOP
- $ make docs
-
-The documentation can be installed either using the `install-docs` target,
-or using the `release_docs` target.
-
-* If you have installed Erlang/OTP using the `install` target, install
- the documentation using the `install-docs` target. Install locations
- determined by `configure` will be used. `$DESTDIR` can be used the
- same way as when doing `make install`.
-
- $ make install-docs
-
-* If you have installed Erlang/OTP using the `release` target, install
- the documentation using the `release_docs` target. You typically want
- to use the same `RELEASE_ROOT` as when invoking `make release`.
-
- $ make release_docs RELEASE_ROOT=<release dir>
-#### Build Issues ####
+### Running ###
-We have sometimes experienced problems with Sun's `java` running out of
-memory when running `fop`. Increasing the amount of memory available
-as follows has in our case solved the problem.
-
- $ export FOP_OPTS="-Xmx<Installed amount of RAM in MB>m"
-
-More information can be found at
-<http://xmlgraphics.apache.org/fop/0.95/running.html#memory>.
-
-### How to Install the Pre-formatted Documentation ###
-
-Pre-formatted [html documentation][] and [man pages][] can be downloaded at
-<http://www.erlang.org/download.html>.
-
-For some graphical tools to find the on-line help you have to install
-the HTML documentation on top of the installed OTP applications, i.e.
-
- $ cd <ReleaseDir>
- $ gunzip -c otp_html_%OTP-VSN%.tar.gz | tar xf -
-
-For `erl -man <page>` to work the Unix manual pages have to be
-installed in the same way, i.e.
-
- $ cd <ReleaseDir>
- $ gunzip -c otp_man_%OTP-VSN%.tar.gz | tar xf -
-
-Where `<ReleaseDir>` is
-
-* `<PrefixDir>/lib/erlang` if you have installed Erlang/OTP using
- `make install`.
-* `$DESTDIR<PrefixDir>/lib/erlang` if you have installed Erlang/OTP
- using `make install DESTDIR=<TmpInstallDir>`.
-* `RELEASE_ROOT` if you have installed using
- `make release RELEASE_ROOT=<ReleaseDir>`.
-
-Support for SMP (Symmetric Multi Processing)
---------------------------------------------
-
-An emulator with SMP support will be built by default on most platforms
-if a usable POSIX thread library or native Windows threads is found.
-
-You can force building of an SMP emulator, by using
-`./configure --enable-smp-support`. However, if configure does not
-automatically enable SMP support, the build is very likely to fail.
-
-Use `./configure --disable-smp-support` if you for some reason do not
-want to have the emulator with SMP support built.
-
-If SMP support is enabled, support for threaded I/O will also be turned on
-(also in the emulator without SMP support).
-
-The `erl` command will automatically start the SMP emulator if the
-computer has more than one logical processor. You can force a start
-of the emulator with SMP support by passing `-smp enable` as
-command line arguments to erl, and you can force a start of the
-emulator without SMP support by passing `-smp disable`.
-
-GS (Graphic System)
--------------------
-
-GS now Tcl/Tk 8.4. It will be searched for when starting GS.
-
-Using HiPE
-----------
+#### Using HiPE ####
HiPE supports the following system configurations:
@@ -619,12 +660,12 @@ HiPE supports the following system configurations:
* FreeBSD: FreeBSD 6.1 and 6.2 in 32-bit and 64-bit modes should work.
- * MacOSX/Darwin: Darwin 9.8.0 in 32-bit mode should work.
+ * OS X/Darwin: Darwin 9.8.0 in 32-bit mode should work.
* PowerPC: All 32-bit 6xx/7xx(G3)/74xx(G4) processors should work. 32-bit
mode on 970 (G5) and POWER5 processors should work.
- * Linux (Yellow Dog) and Mac OSX 10.4 are supported.
+ * Linux (Yellow Dog) and OS X 10.4 are supported.
* SPARC: All UltraSPARC processors running 32-bit user code should work.
@@ -643,11 +684,11 @@ HiPE is automatically enabled on the following systems:
* x86 in 32-bit mode: Linux, Solaris, FreeBSD
* x86 in 64-bit mode: Linux, Solaris, FreeBSD
-* PowerPC: Linux, MacOSX
+* PowerPC: Linux, Mac OSX
* SPARC: Linux
* ARM: Linux
-On other supported systems you need to `./configure --enable-hipe`.
+On other supported systems, see [Advanced Configure][] on how to enable HiPE.
If you are running on a platform supporting HiPE and if you have not disabled
HiPE, you can compile a module into native code like this from the Erlang
@@ -659,7 +700,7 @@ or
1> c(Module, [native|OtherOptions]).
-Using the erlc program, write like this:
+Using the erlc program, write like this
$ erlc +native Module.erl
@@ -674,88 +715,102 @@ Use `hipe:help_options/0` to print out the available options.
1> hipe:help_options().
-Mac OS X (Darwin)
------------------
-
-Make sure that the command `hostname` returns a valid fully qualified host
-name (this is configured in `/etc/hostconfig`).
-
-If you develop linked-in drivers (shared library) you need to link using
-`gcc` and the flags `-bundle -flat_namespace -undefined suppress`. You also
-include `-fno-common` in `CFLAGS` when compiling. Use `.so` as the library
-suffix.
-
-Install `Xcode` from the `AppStore` if it is not already installed.
-
-If you have Xcode 4.3, or later, you will also need to download
-"Command Line Tools" via the Downloads preference pane in Xcode.
-
-### Building with wxErlang ###
+#### Running with GS ####
-If you want to build the `wx` application, you will need to get wxWidgets-3.0 (or later)
-(`wxWidgets-3.0.0.tar.bz2` from <http://sourceforge.net/projects/wxwindows/files/3.0.0/>)
-or get it from github:
- $ git clone [email protected]:wxWidgets/wxWidgets.git
+The `gs` application requires the GUI toolkit Tcl/Tk to run. At least
+version 8.4 is required.
-Be aware that the wxWidgets-3.0 is a new release of wxWidgets, it is not as matured
-as the old releases and the MacOsX port still lags behind the other ports.
-
-Configure and build wxWidgets (on Mavericks - 10.9):
-
- $ ./configure --with-cocoa --prefix=/usr/local
- or without support for old versions and with static libs
- $ ./configure --with-cocoa --prefix=/usr/local --with-macosx-version-min=10.9 --disable-shared
- $ make
- $ sudo make install
- $ export PATH=/usr/local/bin:$PATH
+Known platform issues
+---------------------
-Check that you got the correct wx-config
+* Suse linux 9.1 is shipped with a patched GCC version 3.3.3, having the
+ rpm named `gcc-3.3.3-41`. That version has a serious optimization bug
+ that makes it unusable for building the Erlang emulator. Please
+ upgrade GCC to a newer version before building on Suse 9.1. Suse Linux
+ Enterprise edition 9 (SLES9) has `gcc-3.3.3-43` and is not affected.
- $ which wx-config
+* `gcc-4.3.0` has a serious optimizer bug. It produces an Erlang emulator
+ that will crash immediately. The bug is supposed to be fixed in
+ `gcc-4.3.1`.
-### Finish up ###
+* FreeBSD had a bug which caused `kqueue`/`poll`/`select` to fail to detect
+ that a `writev()` on a pipe has been made. This bug should have been fixed
+ in FreeBSD 6.3 and FreeBSD 7.0. NetBSD and DragonFlyBSD probably have or
+ have had the same bug. More information can be found at:
-Build Erlang
+ * <http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/kern/sys_pipe.c>
+ * <http://lists.freebsd.org/pipermail/freebsd-arch/2007-September/006790.html>
- $ export PATH=/usr/local/bin:$PATH
- $ cd $ERL_TOP
- $ ./configure --enable-shared-zlib
- $ make
- $ sudo make install
+* `getcwd()` on Solaris 9 can cause an emulator crash. If you have
+ async-threads enabled you can increase the stack size of the
+ async-threads as a temporary workaround. See the `+a` command-line
+ argument in the documentation of `erl(1)`. Without async-threads the
+ emulator is not as vulnerable to this bug, but if you hit it without
+ async-threads the only workaround available is to enable async-threads
+ and increase the stack size of the async-threads. Oracle has however
+ released patches that fixes the issue:
-How to Build a Debug Enabled Erlang RunTime System
---------------------------------------------------
+ > Problem Description: 6448300 large mnttab can cause stack overrun
+ > during Solaris 9 getcwd
-After completing all the normal building steps described above a debug
-enabled runtime system can be built. To do this you have to change
-directory to `$ERL_TOP/erts/emulator`.
+ More information can be found at:
+ * <https://getupdates.oracle.com/readme/112874-40>
+ * <https://getupdates.oracle.com/readme/114432-29>
-In this directory execute:
+* `sed` on Solaris seem to have some problems. For example on
+ Solaris 8, the BSD `sed` and XPG4 `sed` should be avoided.
+ Make sure `/bin/sed` or `/usr/bin/sed` is used on the Solaris
+ platform.
- $ make debug FLAVOR=$FLAVOR
-where `$FLAVOR` is either `plain` or `smp`. The flavor options will
-produce a beam.debug and beam.smp.debug executable respectively. The
-files are installed along side with the normal (opt) versions `beam.smp`
-and `beam`.
+Daily Build and Test
+--------------------
+At Ericsson we have a "Daily Build and Test" that runs on:
-To start the debug enabled runtime system execute:
+* Solaris 8, 9
+ * Sparc32
+ * Sparc64
+* Solaris 10
+ * Sparc32
+ * Sparc64
+ * x86
+* SuSE Linux/GNU 9.4, 10.1
+ * x86
+* SuSE Linux/GNU 10.0, 10.1, 11.0
+ * x86
+ * x86\_64
+* openSuSE 11.4 (Celadon)
+ * x86\_64 (valgrind)
+* Fedora 7
+ * PowerPC
+* Fedora 16
+ * x86\_64
+* Gentoo Linux/GNU 1.12.11.1
+ * x86
+* Ubuntu Linux/GNU 7.04, 10.04, 10.10, 11.04, 12.04
+ * x86\_64
+* MontaVista Linux/GNU 4.0.1
+ * PowerPC
+* FreeBSD 10.0
+ * x86
+* OpenBSD 5.4
+ * x86\_64
+* OS X 10.5.8 (Leopard), 10.7.5 (Lion), 10.9.1 (Mavericks)
+ * x86
+* Windows XP SP3, 2003, Vista, 7
+ * x86
+* Windows 7
+ * x86\_64
- $ $ERL_TOP/bin/cerl -debug
+We also have the following "Daily Cross Builds":
-The debug enabled runtime system features lock violation checking,
-assert checking and various sanity checks to help a developer ensure
-correctness. Some of these features can be enabled on a normal beam
-using appropriate configure options.
+* SuSE Linux/GNU 10.1 x86 -> SuSE Linux/GNU 10.1 x86\_64
+* SuSE Linux/GNU 10.1 x86\_64 -> Linux/GNU TILEPro64
-There are other types of runtime systems that can be built as well
-using the similar steps just described.
+and the following "Daily Cross Build Tests":
- $ make $TYPE FLAVOR=$FLAVOR
+* SuSE Linux/GNU 10.1 x86\_64
-where `$TYPE` is `opt`, `gcov`, `gprof`, `debug`, `valgrind`, or `lcnt`.
-These different beam types are useful for debugging and profiling
-purposes.
Authors
-------
@@ -764,6 +819,7 @@ Authors are mostly listed in the application's `AUTHORS` files,
that is `$ERL_TOP/lib/*/AUTHORS` and `$ERL_TOP/erts/AUTHORS`,
not in the individual source files.
+
Copyright and License
---------------------
@@ -784,30 +840,25 @@ under the License.
%CopyrightEnd%
-More Information
-----------------
-
-More information can be found at <http://www.erlang.org>.
-Modifying This Document
------------------------
-
-Before modifying this document you need to have a look at the
-[$ERL_TOP/HOWTO/MARKDOWN.md][] document.
[$ERL_TOP/HOWTO/INSTALL-CROSS.md]: INSTALL-CROSS.md
[$ERL_TOP/HOWTO/INSTALL-WIN32.md]: INSTALL-WIN32.md
[DESTDIR]: http://www.gnu.org/prep/standards/html_node/DESTDIR.html
- [Building in Git]: #How-to-Build-and-Install-ErlangOTP_Building-in-Git
- [Pre-built Source Release]: #How-to-Build-and-Install-ErlangOTP_Prebuilt-Source-Release
- [make and $ERL_TOP]: #How-to-Build-and-Install-ErlangOTP_make-and-ERLTOP
+ [Building in Git]: #Advanced-configuration-and-build-of-ErlangOTP_Building_Within-Git
+ [Advanced Configure]: #Advanced-configuration-and-build-of-ErlangOTP_Configuring
+ [Pre-built Source Release]: #Advanced-configuration-and-build-of-ErlangOTP_Building_Prebuilt-Source-Release
+ [make and $ERL_TOP]: #Advanced-configuration-and-build-of-ErlangOTP_make-and-ERLTOP
[html documentation]: http://www.erlang.org/download/otp_doc_html_%OTP-VSN%.tar.gz
[man pages]: http://www.erlang.org/download/otp_doc_man_%OTP-VSN%.tar.gz
[the released source tar ball]: http://www.erlang.org/download/otp_src_%OTP-VSN%.tar.gz
+ [System Principles]: ../system_principles/system_principles
+ [Known platform issues]: #Known-platform-issues
[native build]: #How-to-Build-and-Install-ErlangOTP
[cross build]: INSTALL-CROSS.md
- [$ERL_TOP/HOWTO/MARKDOWN.md]: MARKDOWN.md
-
- [?TOC]: true
+ [Required Utilities]: #Required-Utilities
+ [Optional Utilities]: #Optional-Utilities
+ [Building on a Mac]: #Advanced-configuration-and-build-of-ErlangOTP_Building_OS-X-Darwin
+ [Building with wxErlang]: #Advanced-configuration-and-build-of-ErlangOTP_Building_Building-with-wxErlang
diff --git a/Makefile.in b/Makefile.in
index bfaf749465..5c96c789dc 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -397,9 +397,9 @@ endif
ERL_TOP=$(ERL_TOP) PATH=$(INST_PATH_PREFIX)"$${PATH}" \
$(MAKE) BUILD_ALL=1 TESTROOT="$(RELEASE_ROOT)" release
ifeq ($(RELEASE_ROOT),)
- $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(OTP_DEFAULT_RELEASE_PATH)"
+ $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(OTP_DEFAULT_RELEASE_PATH)/releases/@OTP_REL@"
else
- $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(RELEASE_ROOT)"
+ $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(RELEASE_ROOT)/releases/@OTP_REL@"
endif
# ---------------------------------------------------------------
@@ -422,6 +422,10 @@ endif
cd $(ERL_TOP)/system/doc && \
PATH=$(BOOT_PREFIX)"$${PATH}" \
ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT="$(RELEASE_ROOT)" DOCGEN=$(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen $@
+ifneq ($(OTP_SMALL_BUILD),true)
+ echo "OTP doc built" > $(ERL_TOP)/make/otp_doc_built
+endif
+
mod2app:
PATH=$(BOOT_PREFIX)"$${PATH}" escript $(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen/priv/bin/xref_mod_app.escript -topdir $(ERL_TOP) -outfile $(ERL_TOP)/make/$(TARGET)/mod2app.xml
@@ -447,6 +451,7 @@ else
$(make_verbose)cd lib && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
$(MAKE) opt BUILD_ALL=true
+ $(V_at)echo "OTP built" > $(ERL_TOP)/make/otp_built
endif
kernel:
$(make_verbose)cd lib/kernel && \
@@ -1038,9 +1043,9 @@ install.Install:
install.otp_version:
ifeq ($(ERLANG_LIBDIR),)
- $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(OTP_DEFAULT_RELEASE_PATH)"
+ $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(OTP_DEFAULT_RELEASE_PATH)/releases/@OTP_REL@"
else
- $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(ERLANG_LIBDIR)"
+ $(INSTALL_DATA) "$(ERL_TOP)/OTP_VERSION" "$(ERLANG_LIBDIR)/releases/@OTP_REL@"
endif
#
diff --git a/OTP_VERSION b/OTP_VERSION
index cfc4bcdf57..b7aba827ff 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-17.0-rc2
+17.1-rc0
diff --git a/README.md b/README.md
index a6a4f11be8..bf80b6391b 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,15 @@ middle-ware to develop these systems. It includes its own distributed
database, applications to interface towards other languages, debugging
and release handling tools.
+ERTS and BEAM
+-------------
+**BEAM** is the name of the virtual machine where all Erlang code is executed.
+Every compiled Erlang file has the suffix .beam. The virtual machine
+is sometimes referred to as the emulator.
+
+**ERTS** is the Erlang Runtime System where the BEAM, kernel and
+standard libraries amongst others are included.
+
More information can be found at [erlang.org] [1].
Building and Installing
@@ -57,7 +66,7 @@ Copyright and License
> %CopyrightBegin%
>
-> Copyright Ericsson AB 2010-2012. All Rights Reserved.
+> Copyright Ericsson AB 2010-2014. All Rights Reserved.
>
> The contents of this file are subject to the Erlang Public License,
> Version 1.1, (the "License"); you may not use this file except in
diff --git a/aclocal.m4 b/aclocal.m4
index c51c26794a..ed492d55ff 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -83,8 +83,10 @@ AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross
AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_LM_CONF, [OSE load module default configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE OSE confd source file])
+AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file])
AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file])
])
@@ -1116,7 +1118,7 @@ case "$THR_LIB_NAME" in
[Define if you have the "ose_spi/ose_spi.h" header file.]))
;;
esac
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
case $host_os in
openbsd*)
# The default stack size is insufficient for our needs
@@ -1220,7 +1222,7 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for functions
dnl
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
AC_CHECK_FUNC(pthread_spin_lock, \
[ethr_have_native_spinlock=yes \
AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index f5800105d3..b57eef57ab 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 f5800105d3..b57eef57ab 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 a3b9f3df8a..42ff7e9c1f 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_bool.beam b/bootstrap/lib/compiler/ebin/beam_bool.beam
index fb414eb9b1..f339e76fce 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_disasm.beam b/bootstrap/lib/compiler/ebin/beam_disasm.beam
index c129898523..f050f39ca0 100644
--- a/bootstrap/lib/compiler/ebin/beam_disasm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_disasm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam
index a619db9d50..2b839bc997 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_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam
index 204a243a5b..8507db5454 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/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam
index 686281467d..ff6f0ea0fd 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/cerl_clauses.beam b/bootstrap/lib/compiler/ebin/cerl_clauses.beam
index ec41e1098b..2357df79f4 100644
--- a/bootstrap/lib/compiler/ebin/cerl_clauses.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_clauses.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_inline.beam b/bootstrap/lib/compiler/ebin/cerl_inline.beam
index cfe023c601..2f7f220ebd 100644
--- a/bootstrap/lib/compiler/ebin/cerl_inline.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_inline.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_trees.beam b/bootstrap/lib/compiler/ebin/cerl_trees.beam
index faafbe20c8..1338631c23 100644
--- a/bootstrap/lib/compiler/ebin/cerl_trees.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_trees.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam
index c13c3a40a6..eb682b953c 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 83554a68a5..f415965277 100644
--- a/bootstrap/lib/compiler/ebin/compiler.app
+++ b/bootstrap/lib/compiler/ebin/compiler.app
@@ -18,7 +18,7 @@
{application, compiler,
[{description, "ERTS CXC 138 10"},
- {vsn, "4.9.4"},
+ {vsn, "5.0"},
{modules, [
beam_a,
beam_asm,
@@ -67,4 +67,6 @@
]},
{registered, []},
{applications, [kernel, stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","hipe-3.10.3","erts-6.0",
+ "crypto-3.3"]}]}.
diff --git a/bootstrap/lib/compiler/ebin/compiler.appup b/bootstrap/lib/compiler/ebin/compiler.appup
index 13c2644004..e73b9aa60c 100644
--- a/bootstrap/lib/compiler/ebin/compiler.appup
+++ b/bootstrap/lib/compiler/ebin/compiler.appup
@@ -15,7 +15,7 @@
%% under the License.
%%
%% %CopyrightEnd%
-{"4.9.4",
+{"5.0",
[{<<".*">>,[{restart_application, compiler}]}],
[{<<".*">>,[{restart_application, compiler}]}]
}.
diff --git a/bootstrap/lib/compiler/ebin/core_lib.beam b/bootstrap/lib/compiler/ebin/core_lib.beam
index 4957fbabb7..bb38837049 100644
--- a/bootstrap/lib/compiler/ebin/core_lib.beam
+++ b/bootstrap/lib/compiler/ebin/core_lib.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam
index dca9efc2f3..e172ec6af7 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 8c5816c52d..121ef65275 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_pp.beam b/bootstrap/lib/compiler/ebin/core_pp.beam
index 5de821db8b..e6e60fb658 100644
--- a/bootstrap/lib/compiler/ebin/core_pp.beam
+++ b/bootstrap/lib/compiler/ebin/core_pp.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/erl_bifs.beam b/bootstrap/lib/compiler/ebin/erl_bifs.beam
index e2dc90c887..27136581c1 100644
--- a/bootstrap/lib/compiler/ebin/erl_bifs.beam
+++ b/bootstrap/lib/compiler/ebin/erl_bifs.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 667d56d6d1..923c4d98fd 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/sys_pre_expand.beam b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
index 6a6216e80f..c0c537f623 100644
--- a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
+++ b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam
index c270802455..3670fd206c 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 0b4228f8a8..ac24c6a6cb 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 ecec470cb0..1e549a41c7 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_kernel_pp.beam b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
index bc67dfe0c6..1798a279b0 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_life.beam b/bootstrap/lib/compiler/ebin/v3_life.beam
index c2363fc62d..0eb0cb6caf 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.beam b/bootstrap/lib/kernel/ebin/application.beam
index 2c8444fb95..6946d17545 100644
--- a/bootstrap/lib/kernel/ebin/application.beam
+++ b/bootstrap/lib/kernel/ebin/application.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 ec9422d41b..8c1622c7d8 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_util.beam b/bootstrap/lib/kernel/ebin/dist_util.beam
index 4cd98bd217..ea09695075 100644
--- a/bootstrap/lib/kernel/ebin/dist_util.beam
+++ b/bootstrap/lib/kernel/ebin/dist_util.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global.beam b/bootstrap/lib/kernel/ebin/global.beam
index 1629089858..d25be3994c 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/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
index 31a54ecef4..46263fae7b 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_config.beam b/bootstrap/lib/kernel/ebin/inet_config.beam
index 03b42209e6..e858ac20f1 100644
--- a/bootstrap/lib/kernel/ebin/inet_config.beam
+++ b/bootstrap/lib/kernel/ebin/inet_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app
index f491239a45..1fd4edd044 100644
--- a/bootstrap/lib/kernel/ebin/kernel.app
+++ b/bootstrap/lib/kernel/ebin/kernel.app
@@ -114,6 +114,7 @@
pg2]},
{applications, []},
{env, [{error_logger, tty}]},
- {mod, {kernel, []}}
+ {mod, {kernel, []}},
+ {runtime_dependencies, ["erts-6.0", "stdlib-2.0", "sasl-2.4"]}
]
}.
diff --git a/bootstrap/lib/kernel/ebin/os.beam b/bootstrap/lib/kernel/ebin/os.beam
index b8b01b31ab..b1b92ca36b 100644
--- a/bootstrap/lib/kernel/ebin/os.beam
+++ b/bootstrap/lib/kernel/ebin/os.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/include/dist.hrl b/bootstrap/lib/kernel/include/dist.hrl
index e32c112e63..77556d1303 100644
--- a/bootstrap/lib/kernel/include/dist.hrl
+++ b/bootstrap/lib/kernel/include/dist.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,3 +37,4 @@
-define(DFLAG_DIST_HDR_ATOM_CACHE,16#2000).
-define(DFLAG_SMALL_ATOM_TAGS, 16#4000).
-define(DFLAG_UTF8_ATOMS, 16#10000).
+-define(DFLAG_MAP_TAG, 16#20000).
diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam
index ac36354796..6f430b8c6c 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/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index 9bb4b6fb1f..7013e29466 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_compile.beam b/bootstrap/lib/stdlib/ebin/erl_compile.beam
index 88da2b246f..e650afbf6d 100644
--- a/bootstrap/lib/stdlib/ebin/erl_compile.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_compile.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_eval.beam b/bootstrap/lib/stdlib/ebin/erl_eval.beam
index 63a3b3aa65..aab2f38b91 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_expand_records.beam b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
index 0e72af0e35..9f7ad7265d 100644
--- a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index 4e3acd3422..9481dc6ef5 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 c1c114dd3c..3d36ee85b7 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 683a07fbd5..ff196f3a45 100644
--- a/bootstrap/lib/stdlib/ebin/erl_pp.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_pp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_event.beam b/bootstrap/lib/stdlib/ebin/gen_event.beam
index 46f1512a24..d45e508d22 100644
--- a/bootstrap/lib/stdlib/ebin/gen_event.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_event.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_fsm.beam b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
index d6719e68d9..bf5bbb7839 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 806c4d58d7..fe95ca0826 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/otp_internal.beam b/bootstrap/lib/stdlib/ebin/otp_internal.beam
index 1146ef9ab8..23b140aeb5 100644
--- a/bootstrap/lib/stdlib/ebin/otp_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/otp_internal.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/pg.beam b/bootstrap/lib/stdlib/ebin/pg.beam
index 65195defbf..31f30461d8 100644
--- a/bootstrap/lib/stdlib/ebin/pg.beam
+++ b/bootstrap/lib/stdlib/ebin/pg.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc_pt.beam b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
index f53c840702..6184f0c295 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/slave.beam b/bootstrap/lib/stdlib/ebin/slave.beam
index ba741284ac..532c46cd38 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/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app
index 31c7222100..1d255fb2c8 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.app
+++ b/bootstrap/lib/stdlib/ebin/stdlib.app
@@ -102,5 +102,8 @@
{registered,[timer_server,rsh_starter,take_over_monitor,pool_master,
dets]},
{applications, [kernel]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["sasl-2.4","kernel-3.0","erts-6.0","crypto-3.3",
+ "compiler-5.0"]}
+]}.
diff --git a/bootstrap/lib/stdlib/ebin/sys.beam b/bootstrap/lib/stdlib/ebin/sys.beam
index 2aed63dc88..1413b822f2 100644
--- a/bootstrap/lib/stdlib/ebin/sys.beam
+++ b/bootstrap/lib/stdlib/ebin/sys.beam
Binary files differ
diff --git a/configure.in b/configure.in
index f6cccb4f4b..be906dcb4e 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-2012. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2014. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -209,7 +209,7 @@ AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]))
AC_ARG_ENABLE(halfword-emulator,
AS_HELP_STRING([--enable-halfword-emulator],
- [enable halfword emulator (only for 64bit builds)]))
+ [enable halfword emulator (only for 64bit builds). Note: Halfword emulator is marked as deprecated and scheduled for removal in future major release.]))
AC_ARG_ENABLE(smp-support,
AS_HELP_STRING([--enable-smp-support], [enable smp support])
diff --git a/erts/Makefile.in b/erts/Makefile.in
index e3db37d3fd..47298cccba 100644
--- a/erts/Makefile.in
+++ b/erts/Makefile.in
@@ -48,6 +48,7 @@ debug opt clean:
( cd $$d && $(MAKE) $@ FLAVOR=$(FLAVOR) ) || exit $$? ; \
fi ; \
done
+ (cd preloaded/src && $(MAKE) ../ebin/erts.app)
# ----------------------------------------------------------------------
# These are "convenience targets", provided as shortcuts for developers
@@ -135,6 +136,10 @@ release:
( cd $$d && $(MAKE) $@ ) || exit $$? ; \
fi ; \
done
+ ( $(MAKE) -f "$(ERL_TOP)/make/otp_released_app.mk" \
+ APP_PWD="$(ERL_TOP)/erts" APP_VSN=VSN APP=erts \
+ TESTROOT="$(TESTROOT)" update) \
+ || exit $$?
.PHONY: release_docs
release_docs:
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4
index c51c26794a..ed492d55ff 100644
--- a/erts/aclocal.m4
+++ b/erts/aclocal.m4
@@ -83,8 +83,10 @@ AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross
AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_LM_CONF, [OSE load module default configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE OSE confd source file])
+AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file])
AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file])
])
@@ -1116,7 +1118,7 @@ case "$THR_LIB_NAME" in
[Define if you have the "ose_spi/ose_spi.h" header file.]))
;;
esac
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
case $host_os in
openbsd*)
# The default stack size is insufficient for our needs
@@ -1220,7 +1222,7 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for functions
dnl
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
AC_CHECK_FUNC(pthread_spin_lock, \
[ethr_have_native_spinlock=yes \
AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
diff --git a/erts/configure.in b/erts/configure.in
index 074882532f..04303da4f8 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. -*-m4-*-
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1997-2013. All Rights Reserved.
+dnl Copyright Ericsson AB 1997-2014. All Rights Reserved.
dnl
dnl The contents of this file are subject to the Erlang Public License,
dnl Version 1.1, (the "License"); you may not use this file except in
@@ -144,7 +144,7 @@ AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]),
AC_ARG_ENABLE(halfword-emulator,
AS_HELP_STRING([--enable-halfword-emulator],
- [enable halfword emulator (only for 64bit builds)]),
+ [enable halfword emulator (only for 64bit builds). Note: Halfword emulator is marked as deprecated and scheduled for removal in future major release.]),
[ case "$enableval" in
no) enable_halfword_emualtor=no ;;
*) enable_halfword_emulator=yes ;;
@@ -214,9 +214,6 @@ AC_ARG_ENABLE(native-libs,
AS_HELP_STRING([--enable-native-libs],
[compile Erlang libraries to native code]))
-AC_ARG_ENABLE(tsp,
-AS_HELP_STRING([--enable-tsp], [compile tsp app]))
-
AC_ARG_ENABLE(fp-exceptions,
AS_HELP_STRING([--enable-fp-exceptions],
[use hardware floating point exceptions (default if hipe enabled)]),
@@ -366,6 +363,11 @@ AC_DEFINE_UNQUOTED(ASSUMED_CACHE_LINE_SIZE,
$with_assumed_cache_line_size,
[Assumed cache-line size (in bytes)])
+AC_ARG_ENABLE(systemd,
+AS_HELP_STRING([--enable-systemd], [enable systemd support in epmd]),
+[],
+[enable_systemd=no])
+
dnl Magic test for clearcase.
OTP_RELEASE=
if test "${ERLANG_COMMERCIAL_BUILD}" != ""; then
@@ -456,10 +458,10 @@ AS_HELP_STRING([--enable-static-drivers], [comma seperated list of linked-in dri
AC_SUBST(STATIC_DRIVERS)
AC_ARG_WITH(ets-write-concurrency-locks,
- AS_HELP_STRING([--with-ets-write-concurrency-locks={8,16,32,64,128,256}],
- [specify how many locks the write_concurrency option for ets should use.])
- AS_HELP_STRING([--without-ets-write-concurrency-locks],
- [use the default number of write_concurrency locks (default)]))
+AS_HELP_STRING([--with-ets-write-concurrency-locks={8|16|32|64|128|256}],
+ [specify how many locks the write_concurrency option for ets should use.])
+AS_HELP_STRING([--without-ets-write-concurrency-locks],
+ [use the default number of write_concurrency locks (default)]))
if test X"$with_ets_write_concurrency_locks" != X""; then
AC_DEFINE_UNQUOTED(ERTS_DB_HASH_LOCK_CNT,$with_ets_write_concurrency_locks,
@@ -816,6 +818,16 @@ if test "$enable_halfword_emulator" = "yes"; then
[Define if building a halfword-heap 64bit emulator])
ENABLE_ALLOC_TYPE_VARS="$ENABLE_ALLOC_TYPE_VARS halfword"
AC_MSG_RESULT([yes])
+
+ test -f "$ERL_TOP/erts/CONF_INFO" ||
+ echo "" > "$ERL_TOP/erts/CONF_INFO"
+ cat >> $ERL_TOP/erts/CONF_INFO <<EOF
+
+ The HALFWORD emulator has been enabled.
+ This is a DEPRECATED feature scheduled for removal
+ in a future major release.
+
+EOF
else
AC_MSG_ERROR(no; halfword emulator not supported on this architecture)
fi
@@ -1151,7 +1163,7 @@ fi
AC_SUBST(ERTS_BUILD_SMP_EMU)
-AC_CHECK_FUNCS([posix_fadvise])
+AC_CHECK_FUNCS([posix_fadvise closefrom])
AC_CHECK_HEADERS([linux/falloc.h])
dnl * Old glibcs have broken fallocate64(). Make sure not to use it.
AC_CACHE_CHECK([whether fallocate() works],i_cv_fallocate_works,[
@@ -1668,6 +1680,30 @@ AC_CHECK_MEMBERS([struct ifreq.ifr_enaddr], [], [],
])
dnl ----------------------------------------------------------------------
+dnl Check the availability of systemd
+dnl ----------------------------------------------------------------------
+if test x"$enable_systemd" != x"no"; then
+
+systemd_daemon_save_LIBS=$LIBS
+LIBS=
+AC_SEARCH_LIBS(sd_listen_fds,[systemd systemd-daemon],
+ [have_sd_listen_fds=yes],[have_sd_listen_fds=no],$systemd_daemon_save_LIBS)
+AC_CHECK_HEADERS(systemd/sd-daemon.h,
+ [have_systemd_sd_daemon_h=yes],[have_systemd_sd_daemon_h=no])
+
+if test x"$have_sd_listen_fds" = x"yes" && \
+ test x"$have_systemd_sd_daemon_h" = x"yes"; then
+ AC_DEFINE([HAVE_SYSTEMD_DAEMON],[1],[Define if you have systemd daemon])
+ SYSTEMD_DAEMON_LIBS=$LIBS
+elif test x"$enable_systemd" = x"yes"; then
+ AC_MSG_FAILURE([--enable-systemd was given, but test for systemd failed])
+fi
+LIBS=$systemd_daemon_save_LIBS
+fi
+AC_SUBST(SYSTEMD_DAEMON_LIBS)
+
+
+dnl ----------------------------------------------------------------------
dnl Check the availability for libdlpi
dnl ----------------------------------------------------------------------
AC_CHECK_LIB(dlpi, dlpi_open)
@@ -3418,16 +3454,6 @@ dnl (using the --disable, --enable and --with switches).
dnl ----------------------------------------------------------------------
#
-# Check if we should compile TSP app
-#
-
-TSP_APP=
-if test X${enable_tsp} = Xyes; then
- TSP_APP=tsp
-fi
-AC_SUBST(TSP_APP)
-
-#
# Check if we should enable HiPE.
#
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 4aa3033f40..f8f4d14436 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -495,7 +495,7 @@
<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>
</item>
- <tag><c><![CDATA[+c]]></c></tag>
+ <tag><marker id="+c"><c><![CDATA[+c]]></c></marker></tag>
<item>
<p>Disable compensation for sudden changes of system time.</p>
<p>Normally, <c><![CDATA[erlang:now/0]]></c> will not immediately reflect
@@ -510,6 +510,9 @@
reflect the current system time. Note that timers are based
on <c><![CDATA[erlang:now/0]]></c>. If the system time jumps, timers
then time out at the wrong time.</p>
+ <p><em>NOTE</em>: You can check whether the adjustment is enabled or
+ disabled by calling
+ <seealso marker="erlang#system_info_tolerant_timeofday">erlang:system_info(tolerant_timeofday)</seealso>.</p>
</item>
<tag><c><![CDATA[+d]]></c></tag>
<item>
@@ -536,7 +539,7 @@
<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.</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>
</item>
<tag><c><![CDATA[+fnu[{w|i|e}]]]></c></tag>
<item>
@@ -555,7 +558,7 @@
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.</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>
</item>
<tag><c><![CDATA[+fna[{w|i|e}]]]></c></tag>
<item>
@@ -572,7 +575,7 @@
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.</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>
</item>
<tag><c><![CDATA[+hms Size]]></c></tag>
<item>
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index 8da1836da7..ad37813ac0 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -315,10 +315,13 @@
<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 refuse to load a driver if the major
+ The runtime system will normally refuse 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.</p>
+ 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,
diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml
index f91ed78122..fa083db4c7 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>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -573,6 +573,33 @@
</section>
<section>
+ <marker id="MAP_EXT"/>
+ <title>MAP_EXT</title>
+
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">4</cell>
+ <cell align="center">N</cell>
+ </row>
+ <row>
+ <cell align="center">116</cell>
+ <cell align="center">Arity</cell>
+ <cell align="center">Pairs</cell>
+ </row>
+ <tcaption></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
+ 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:
+ <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>
+ </section>
+
+ <section>
<marker id="NIL_EXT"/>
<title>NIL_EXT</title>
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 8b19725c02..6b1f4cccf8 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -316,6 +316,32 @@ ok
<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>
+
<tag>Dirty NIFs</tag>
<item><p><marker id="dirty_nifs"/><em>Note that the dirty NIF functionality
is experimental</em> and that you have to enable support for dirty
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index aeded7c719..9ad42374bf 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -887,7 +887,7 @@
<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>
+ output. On OSE the term is printed to the ramlog.</p>
<warning>
<p>This BIF is intended for debugging only.</p>
</warning>
@@ -1784,6 +1784,15 @@ os_prompt% </pre>
</desc>
</func>
<func>
+ <name name="is_map" arity="1"/>
+ <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 returns <c>false</c>.</p>
+ <p>Allowed in guard tests.</p>
+ </desc>
+ </func>
+ <func>
<name name="is_number" arity="1"/>
<fsummary>Check whether a term is a number</fsummary>
<desc>
@@ -2220,6 +2229,17 @@ os_prompt% </pre>
</desc>
</func>
<func>
+ <name name="map_size" arity="1"/>
+ <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>.</p>
+ <pre>
+> <input>map_size(#{a=>1, b=>2, c=>3}).</input>
+3</pre>
+ <p>Allowed in guard tests.</p>
+ </desc>
+ </func>
+ <func>
<name name="max" arity="2"/>
<fsummary>Return the largest of two term</fsummary>
<desc>
@@ -4822,6 +4842,8 @@ true</pre>
<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>The <c>monitor</c> option is currently not supported by
+ <c>spawn_opt/5</c>.</p></note>
</desc>
</func>
<func>
@@ -6121,8 +6143,8 @@ ok
<c>erlang:system_info()</c> argument giving the exact OTP
version. This since the exact OTP version in the general case
is hard to determine. For more information see
- <seealso marker="doc/installation_guide:otp_version">the
- documentation of the OTP version in the installation
+ <seealso marker="doc/system_principles:versions">the
+ documentation of versions in the system principles
guide</seealso>.</p>
</item>
<tag><marker id="system_info_port_parallelism"><c>port_parallelism</c></marker></tag>
@@ -6273,6 +6295,13 @@ ok
(<seealso marker="erts:erl_driver#driver_async">driver_async()</seealso>)
as an integer.</p>
</item>
+ <tag><marker id="system_info_tolerant_timeofday"><c>tolerant_timeofday</c></marker></tag>
+ <item>
+ <p>Returns whether compensation for sudden changes of system
+ time is <c>enabled</c> or <c>disabled</c>.</p>
+ <p>See also <seealso marker="erts:erl#+c">+c</seealso>
+ command line flag.</p>
+ </item>
<tag><c>trace_control_word</c></tag>
<item>
<p>Returns the value of the node's trace control word.
diff --git a/erts/doc/src/escript.xml b/erts/doc/src/escript.xml
index d2b09d4515..9159d68f60 100644
--- a/erts/doc/src/escript.xml
+++ b/erts/doc/src/escript.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>2007</year><year>2013</year>
+ <year>2007</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -92,6 +92,18 @@ $ <input>escript factorial 5</input> </pre>
marker="stdlib:epp#encoding">encoding</seealso> 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:
+<code>io:setopts([{encoding, unicode}])</code></p>
+ <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>
@@ -141,8 +153,9 @@ halt(1).</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 here, but if there is a valid encoding comment on
- the second line it takes precedence.</p>
+ encoding by including a encoding comment here, but if there
+ is a valid encoding comment 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
@@ -163,7 +176,7 @@ halt(1).</pre>
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 compile, this will compile the script
+ <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>
@@ -223,8 +236,13 @@ factorial 5 = 120
<v>EmuArgs = string() | 'undefined'</v>
<v>Body = {source, SourceCode}
| {beam, BeamCode}
- | {archive, ZipArchive}</v>
- <v>SourceCode = BeamCode = ZipArchive = binary()</v>
+ | {archive, ZipArchive}
+ | {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>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>
</type>
<desc>
<p>The <marker id="create_2"></marker> <c>create/2</c>
@@ -239,7 +257,7 @@ factorial 5 = 120
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 emu_args to set some emulator
+ 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>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index b4ebef72f4..68feaa027a 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -30,6 +30,738 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 6.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix broken system monitoring of <c>large_heap</c> for
+ non-smp VM. No message for <c>large_heap</c> was ever
+ sent on non-smp VM. Bug exist since R16B.</p>
+ <p>
+ Own Id: OTP-11852</p>
+ </item>
+ <item>
+ <p>
+ Fixed type spec of <c>erlang:system_info/1</c>.</p>
+ <p>
+ Own Id: OTP-11859 Aux Id: OTP-11615 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 6.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The option dupnames did not work as intended in re. When
+ looking for names with {capture, [Name, ...]}, re:run
+ returned a random instance of the match for that name,
+ instead of the leftmost matching instance, which was what
+ the documentation stated. This is now corrected to adhere
+ to the documentation. The option {capture,all_names}
+ along with a re:inspect/2 function is also added to
+ further help in using named subpatterns.</p>
+ <p>
+ Own Id: OTP-11205</p>
+ </item>
+ <item>
+ <p>
+ Allow loading of NIF library with unicode path name</p>
+ <p>
+ Own Id: OTP-11408</p>
+ </item>
+ <item>
+ <p>
+ Allow loading of driver with unicode path name</p>
+ <p>
+ Own Id: OTP-11549</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug where starting Erlang without having an open
+ stdin on fd 0 would sometimes deadlock the emulator when
+ terminating.</p>
+ <p>
+ Own Id: OTP-11558</p>
+ </item>
+ <item>
+ <p>
+ The option '-names' in epmd now works on Windows (Thanks
+ to Johannes Weißl)</p>
+ <p>
+ Own Id: OTP-11565</p>
+ </item>
+ <item>
+ <p>
+ Correction of the examples in escript documentation.
+ (Thanks to Pierre Fenoll).</p>
+ <p>
+ Own Id: OTP-11577</p>
+ </item>
+ <item>
+ <p>
+ Fix bs_get_integer instruction</p>
+ <p>
+ The instruction bs_get_integer could unnecessarily
+ trigger a garbage collection in failure cases which is
+ unwanted or outright dangerous.</p>
+ <p>
+ Ex:</p>
+ <p>
+ &lt;&lt;X:Sz,_/bits&gt;&gt; = &lt;&lt;"some
+ binary"&gt;&gt;</p>
+ <p>
+ Previously, if Sz induced X to a bignum it would reserved
+ memory size this on the heap via a garbage collection
+ before checking if the size could actually match.</p>
+ <p>
+ It will now check the binary size before triggering a
+ collection.</p>
+ <p>
+ Own Id: OTP-11581</p>
+ </item>
+ <item>
+ <p>
+ Remove heap space overestimation in <c>binary_to_term</c>
+ (and remote message reception) for integers in the
+ intervals [-2147483648,-1] and [256,2147483647] on 64-bit
+ emulators.</p>
+ <p>
+ Own Id: OTP-11585</p>
+ </item>
+ <item>
+ <p>
+ Add support for detecting the separate tinfo library from
+ ncurses (Thanks to Dirkjan Ochtman)</p>
+ <p>
+ Own Id: OTP-11590</p>
+ </item>
+ <item>
+ <p>
+ Deprecation warning for system_flag(cpu_topology) has
+ been extended for removal in OTP 18 (Thanks to Steve
+ Vinoski for the update)</p>
+ <p>
+ Own Id: OTP-11602</p>
+ </item>
+ <item>
+ <p>
+ Documentation improvement regarding some awkward wording
+ around the +spp flag. (Thanks to Brian L. Troutwine )</p>
+ <p>
+ Own Id: OTP-11607</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug where sendfile would return the wrong error
+ code for a remotely closed socket if the socket was in
+ passive mode. (Thanks to Vincent Siliakus for reporting
+ the bug.)</p>
+ <p>
+ Own Id: OTP-11614</p>
+ </item>
+ <item>
+ <p>
+ Increase garbage collection tenure rate</p>
+ <p>The garbage collector tries to maintain the previous
+ heap block size during a minor gc, i.e. 'need' is not
+ utilized in determining the size of the new heap, instead
+ it relies on tenure and garbage to be sufficiently
+ large.</p>
+ <p>In instances during intense growing with exclusively
+ live data on the heap coupled with delayed tenure,
+ fullsweeps would be triggered directly after a minor gc
+ to make room for 'need' since the new heap would be
+ full.</p>
+ <p>To remedy this, the tenure of terms on the minor heap
+ will always happen (if it is below the high watermark)
+ instead of every other minor gc.</p>
+ <p>Characteristics Impact: Reduced CPU-time spent in
+ garbage collection but may infer delays in collecting
+ garbage from the heap. Tweak 'fullsweep_after' options to
+ increase gc pressure if needed.</p>
+ <p>
+ Own Id: OTP-11617</p>
+ </item>
+ <item>
+ <p>
+ Fix bug when comparing integers with floats larger than
+ 2^992. The bug could potentially cause memory corruption
+ on 32-bit emulators.</p>
+ <p>
+ Own Id: OTP-11618</p>
+ </item>
+ <item>
+ <p>
+ Cross-compilation fixes for TileraMDE-3.0.1.125620</p>
+ <p>
+ Own Id: OTP-11635</p>
+ </item>
+ <item>
+ <p>
+ sendfile no longer uses async threads by default</p>
+ <p>
+ This has been done because a slow client attack is
+ possible if the async thread pool is used. The scenario
+ is:</p>
+ <p>
+ Client does a request for a file and then slowly receives
+ the file one byte at a time. This will eventually fill
+ the async thread pool with blocking sendfile operations
+ and thus starving the vm of all file operations.</p>
+ <p>
+ If you still want to use the async threads pool for
+ sendfile an option to enable it has been introduced.</p>
+ <p>
+ Thanks to Christopher Faulet for identifying this
+ vulnerability.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11639</p>
+ </item>
+ <item>
+ <p>
+ Do proper rollback of calls to
+ <c>enif_open_resource_type</c> when load/upgrade
+ callbacks of NIF library return failure.</p>
+ <p>
+ Own Id: OTP-11722</p>
+ </item>
+ <item>
+ <p>
+ Changed the default configuration when configuring with
+ <c>$ERL_TOP/configure</c> to be the same as when
+ configuring with <c>$ERL_TOP/otp_build configure</c>.</p>
+ <p>
+ Previously floating point exceptions got enabled by
+ default on Linux when HiPE was enabled when configuring
+ with <c>$ERL_TOP/configure</c>, but not when configuring
+ with <c>$ERL_TOP/otp_build configure</c>. The default is
+ now in both cases not to use floating point exceptions
+ since there still exist unresolved issues with floating
+ point exceptions on Linux.</p>
+ <p>
+ For more information see <seealso
+ marker="doc/installation_guide:INSTALL"><c>$ERL_TOP/HOWTO/INSTALL.md</c></seealso>.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11723</p>
+ </item>
+ <item>
+ <p>
+ A comment in erl_db_tree.c no longer differ from the
+ code. (Thanks to Cobus Carstens)</p>
+ <p>
+ Own Id: OTP-11793</p>
+ </item>
+ <item>
+ <p>
+ Fix epmd debug functionality for VxWorks (Thanks to Jay
+ True)</p>
+ <p>
+ Own Id: OTP-11808</p>
+ </item>
+ <item>
+ <p>
+ Use closefrom/2 when available in child_setup (Thanks to
+ Rick Reed and Anthony Ramine)</p>
+ <p>
+ Own Id: OTP-11809</p>
+ </item>
+ <item>
+ <p>
+ Fix dtrace/systemtap bug where the probe arguments would
+ be concatenated due to faulty length calculation. </p>
+ <p>
+ Thanks to Michal Ptaszek and Scott Lystig Fritchie</p>
+ <p>
+ Own Id: OTP-11816</p>
+ </item>
+ <item>
+ <p>
+ It is now better documented that the <c>+fn*</c> flags to
+ <c>erl</c> also affect how command line parameters and
+ environment variables are read. (Thanks to Vlad
+ Dumitrescu)</p>
+ <p>
+ Own Id: OTP-11818</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Options to set match_limit and match_limit_recursion are
+ added to re:run. The option report_errors is also added
+ to get more information when re:run fails due to limits
+ or compilation errors.</p>
+ <p>
+ Own Id: OTP-10285</p>
+ </item>
+ <item>
+ <p> Dialyzer's <c>unmatched_return</c> warnings have been
+ corrected. </p>
+ <p>
+ Own Id: OTP-10908</p>
+ </item>
+ <item>
+ <p>
+ A common case is to wrap an argument to
+ <c>list_to_binary/1</c> in a list to ensure conversion
+ can happen even though the argument may already be a
+ binary. Take special care of this case and do not copy
+ binary.</p>
+ <p>
+ Impact: May cause incompatibility since a single binary
+ is no longer copied. Use <c>binary:copy/1,2</c> instead.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11082</p>
+ </item>
+ <item>
+ <p>
+ Make erlang:open_port/2 spawn and spawn_executable handle
+ unicode.</p>
+ <p>
+ Own Id: OTP-11105</p>
+ </item>
+ <item>
+ <p>
+ Handle unicode (widestring) in erl, erlc, heart, etc on
+ windows.</p>
+ <p>
+ Own Id: OTP-11135</p>
+ </item>
+ <item>
+ <p>
+ The version of the PCRE library Used by Erlang's re
+ module is raised to 8.33 from 7.6. This means, among
+ other things, better Unicode and Unicode Character
+ Properties support. New options connected to PCRE 8.33
+ are also added to the re module (ucd, notempty_atstart,
+ no_start_optimize). PCRE has extended the regular
+ expression syntax between 7.6 and 8.33, why this imposes
+ a potential incompatibility. Only very complicated
+ regular expressions may be affected, but if you know you
+ are using obscure features, please test run your regular
+ expressions and verify that their behavior has not
+ changed.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11204</p>
+ </item>
+ <item>
+ <p>Filenames containing UTF-8 encoded characters can now
+ be handled by erlc.</p>
+ <p>If you have set the <c>ERLC_EMULATOR</c> environment
+ variable, note that <c>erlc</c> in OTP 17 will only work
+ with <c>erl</c> in OTP 17 since the protocol between the
+ <c>erlc</c> program and the <c>erl_compile</c> module has
+ changed.</p>
+ <p>
+ Own Id: OTP-11248</p>
+ </item>
+ <item>
+ <p>
+ By giving --enable-static-{nifs,drivers} to configure it
+ is now possible to statically linking of nifs and drivers
+ to the main Erlang VM binary. At the moment only the asn1
+ and crypto nifs of the Erlang/OTP nifs and drivers have
+ been prepared to be statically linked. For more details
+ see the Installation Guide in the System documentation.</p>
+ <p>
+ Own Id: OTP-11258</p>
+ </item>
+ <item>
+ <p>
+ Erlang/OTP has been ported to the realtime operating
+ system OSE. The port supports both smp and non-smp
+ emulator. For details around the port and how to started
+ see the User's Guide in the <seealso
+ marker="ose:ose_intro">ose</seealso> application. </p>
+ <p>
+ Note that not all parts of Erlang/OTP has been ported. </p>
+ <p>
+ Notable things that work are: non-smp and smp emulators,
+ OSE signal interaction, crypto, asn1, run_erl/to_erl,
+ tcp, epmd, distribution and most if not all non-os
+ specific functionality of Erlang.</p>
+ <p>
+ Notable things that does not work are: udp/sctp, os_mon,
+ erl_interface, binding of schedulers.</p>
+ <p>
+ Own Id: OTP-11334</p>
+ </item>
+ <item>
+ <p>
+ Add the {active,N} socket option for TCP, UDP, and SCTP,
+ where N is an integer in the range -32768..32767, to
+ allow a caller to specify the number of data messages to
+ be delivered to the controlling process. Once the
+ socket's delivered message count either reaches 0 or is
+ explicitly set to 0 with inet:setopts/2 or by including
+ {active,0} as an option when the socket is created, the
+ socket transitions to passive ({active, false}) mode and
+ the socket's controlling process receives a message to
+ inform it of the transition. TCP sockets receive
+ {tcp_passive,Socket}, UDP sockets receive
+ {udp_passive,Socket} and SCTP sockets receive
+ {sctp_passive,Socket}. </p>
+ <p>
+ The socket's delivered message counter defaults to 0, but
+ it can be set using {active,N} via any gen_tcp, gen_udp,
+ or gen_sctp function that takes socket options as
+ arguments, or via inet:setopts/2. New N values are added
+ to the socket's current counter value, and negative
+ numbers can be used to reduce the counter value.
+ Specifying a number that would cause the socket's counter
+ value to go above 32767 causes an einval error. If a
+ negative number is specified such that the counter value
+ would become negative, the socket's counter value is set
+ to 0 and the socket transitions to passive mode. If the
+ counter value is already 0 and inet:setopts(Socket,
+ [{active,0}]) is specified, the counter value remains at
+ 0 but the appropriate passive mode transition message is
+ generated for the socket.</p>
+ <p>
+ Thanks to Steve Vinoski</p>
+ <p>
+ Own Id: OTP-11368</p>
+ </item>
+ <item>
+ <p>
+ A new optional scheduler utilization balancing mechanism
+ has been introduced. For more information see the
+ <seealso marker="erl#+sub"><c>+sub</c></seealso> command
+ line argument.</p>
+ <p>
+ Characteristics impact: None, when not enabled. When
+ enabled, changed timing in the system, normally a small
+ overhead due to measuring of utilization and calculating
+ balancing information. On some systems, such as old
+ Windows systems, the overhead can be quite substantial.
+ This time measurement overhead highly depend on the
+ underlying primitives provided by the OS.</p>
+ <p>
+ Own Id: OTP-11385</p>
+ </item>
+ <item>
+ <p>
+ A call to either the <c>garbage_collect/1</c> BIF or the
+ <c>check_process_code/2</c> BIF may trigger garbage
+ collection of another processes than the process calling
+ the BIF. The previous implementations performed these
+ kinds of garbage collections without considering the
+ internal state of the process being garbage collected. In
+ order to be able to more easily and more efficiently
+ implement yielding native code, these types of garbage
+ collections have been rewritten. A garbage collection
+ like this is now triggered by an asynchronous request
+ signal, the actual garbage collection is performed by the
+ process being garbage collected itself, and finalized by
+ a reply signal to the process issuing the request. Using
+ this approach processes can disable garbage collection
+ and yield without having to set up the heap in a state
+ that can be garbage collected.</p>
+ <p>
+ The <seealso
+ marker="erts:erlang#garbage_collect/2"><c>garbage_collect/2</c></seealso>,
+ and <seealso
+ marker="erts:erlang#check_process_code/3"><c>check_process_code/3</c></seealso>
+ BIFs have been introduced. Both taking an option list as
+ last argument. Using these, one can issue asynchronous
+ requests.</p>
+ <p>
+ <c>code:purge/1</c> and <c>code:soft_purge/1</c> have
+ been rewritten to utilize asynchronous
+ <c>check_process_code</c> requests in order to
+ parallelize work.</p>
+ <p>
+ Characteristics impact: A call to the
+ <c>garbage_collect/1</c> BIF or the
+ <c>check_process_code/2</c> BIF will normally take longer
+ time to complete while the system as a whole wont be as
+ much negatively effected by the operation as before. A
+ call to <c>code:purge/1</c> and <c>code:soft_purge/1</c>
+ may complete faster or slower depending on the state of
+ the system while the system as a whole wont be as much
+ negatively effected by the operation as before.</p>
+ <p>
+ Own Id: OTP-11388 Aux Id: OTP-11535, OTP-11648 </p>
+ </item>
+ <item>
+ <p>
+ Cleanup 'Buckets' and 'Time left' fields in crashdump to
+ ease parsing.</p>
+ <p>
+ Own Id: OTP-11419</p>
+ </item>
+ <item>
+ <p>
+ Add sync option to file:open/2.</p>
+ <p>
+ The sync option adds the POSIX O_SYNC flag to the open
+ system call on platforms that support the flag or its
+ equivalent, e.g., FILE_FLAG_WRITE_THROUGH on Windows. For
+ platforms that don't support it, file:open/2 returns
+ {error, enotsup} if the sync option is passed in. Thank
+ to Steve Vinoski and Joseph Blomstedt</p>
+ <p>
+ Own Id: OTP-11498</p>
+ </item>
+ <item>
+ <p>
+ erlang:binary_to_term will now cost an appropriate amount
+ of reductions and will interrupt (yield) for reschedule
+ if the term is big. This avoids too long schedules when
+ binary_to_term is used. (Thanks to Svante Karlsson for
+ the original patch)</p>
+ <p>
+ Impact: Programs running binary_to_term on large binaries
+ will run more smoothly, but rescheduling will impact the
+ single process performance of the BIF. Single threaded
+ benchmarks might show degraded performance of the BIF,
+ while general system behaviour will be improved.</p>
+ <p>
+ Own Id: OTP-11535 Aux Id: OTP-11388 </p>
+ </item>
+ <item>
+ <p>
+ Added high resolution icon for windows. (Thanks to Daniel
+ Goertz for the inspiration.)</p>
+ <p>
+ Own Id: OTP-11560</p>
+ </item>
+ <item>
+ <p>
+ Migration of memory carriers has been enabled by default
+ on all ERTS internal memory allocators based on the
+ <seealso
+ marker="erts_alloc#alloc_util"><c>alloc_util</c></seealso>
+ framework except for <c>temp_alloc</c>. That is, <seealso
+ marker="erts_alloc#M_acul"><c>+M&lt;S&gt;acul
+ de</c></seealso> is default for these allocators. Note
+ that this also implies changed allocation strategies for
+ all of these allocators. They will all now use the
+ "address order first fit carrier best fit" strategy.</p>
+ <p>
+ By passing <c>+Muacul 0</c> on the command line, all
+ configuration changes made by this change will be
+ reverted.</p>
+ <p>
+ Characteristics impact: Improved memory characteristics
+ with a smaller memory footprint at the expense of a quite
+ small performance cost.</p>
+ <p>
+ Own Id: OTP-11604 Aux Id: OTP-10279 </p>
+ </item>
+ <item>
+ <p>A clarification has been added to the documentation of
+ <c>-on_load()</c> in the Reference Manual that it is only
+ recommended for loading NIF libraries.</p>
+ <p>
+ Own Id: OTP-11611</p>
+ </item>
+ <item>
+ <p><c>+fnaw</c> is now default when starting the
+ emulator; it used to be <c>+fnl</c>.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11612</p>
+ </item>
+ <item>
+ <p>
+ EEP43: New data type - Maps</p>
+ <p>
+ With Maps you may for instance: <taglist> <item><c>M0 =
+ #{ a =&gt; 1, b =&gt; 2}, % create
+ associations</c></item> <item><c>M1 = M0#{ a := 10 }, %
+ update values</c></item> <item><c>M2 = M1#{ "hi" =&gt;
+ "hello"}, % add new associations</c></item> <item><c>#{
+ "hi" := V1, a := V2, b := V3} = M2. % match keys with
+ values</c></item> </taglist></p>
+ <p>
+ For information on how to use Maps please see the
+ <seealso marker="doc/reference_manual:maps">Reference
+ Manual</seealso>.</p>
+ <p>
+ The current implementation is without the following
+ features: <taglist> <item>No variable keys</item>
+ <item>No single value access</item> <item>No map
+ comprehensions</item> </taglist></p>
+ <p>
+ Note that Maps is <em>experimental</em> during OTP 17.0.</p>
+ <p>
+ Own Id: OTP-11616</p>
+ </item>
+ <item>
+ <p>
+ The previously deprecated driver API function
+ <c>driver_async_cancel()</c> has been removed. Due to
+ this, the driver API version has been bumped to 3.0.</p>
+ <p>
+ Thanks to Steve Vinoski.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11628</p>
+ </item>
+ <item>
+ <p>
+ Experimental "dirty scheduler" functionality has been
+ introduced. In order to try the functionality out, you
+ need to pass the command line argument
+ <c>--enable-dirty-schedulers</c> to <c>configure</c> when
+ building the system.</p>
+ <p>
+ Dirty schedulers can currently only be used by NIFs on a
+ system with SMP support. More information can be found in
+ the <seealso
+ marker="erl_nif#dirty_nifs"><c>erl_nif(3)</c></seealso>
+ documentation, the <seealso
+ marker="erl"><c>erl(1)</c></seealso> documentation, and
+ in the git commit comment of commit
+ 'c1c03ae4ee50e58b7669ea88ec4d29c6b2b67c7b'.</p>
+ <p>
+ Note that the functionality is <em>experimental</em>, and
+ <em>not supported</em>. This functionality <em>will</em>
+ be subject to backward incompatible changes. You should
+ <em>not</em> enable the dirty scheduler functionality on
+ production systems. It is only provided for testing.</p>
+ <p>
+ Thanks to Steve Vinoski.</p>
+ <p>
+ Own Id: OTP-11629</p>
+ </item>
+ <item>
+ <p>
+ Improve reduction cost and yielding of
+ <c>term_to_binary</c>. The reduction cost is increased
+ and garbage collection is disabled during yield.</p>
+ <p>
+ Impact: Improves system responsiveness when
+ <c>term_to_binary</c> is called with large terms without
+ significant degradation of single threaded performance.</p>
+ <p>
+ Own Id: OTP-11648 Aux Id: OTP-11388 </p>
+ </item>
+ <item>
+ <p>
+ By default, the system's version of zlib will be used,
+ provided its version is 1.2.4 or higher; otherwise the
+ built-in zlib will be used. The built-in version of zlib
+ has been bumped to 1.2.8. (Use the
+ <c>--enable-builtin-zlib</c> option to <c>configure</c>
+ to force the use of the built-in zlib.)</p>
+ <p>
+ Own Id: OTP-11669</p>
+ </item>
+ <item>
+ <p>
+ The default float encoding in binary_to_term and
+ external_size has been changed to use minor_mode 1
+ instead of 0.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11738</p>
+ </item>
+ <item>
+ <p>
+ Introduced the <c>configure</c> option
+ <c>--with-assumed-cache-line-size=SIZE</c>. For more
+ information see <seealso
+ marker="doc/installation_guide:INSTALL"><c>$ERL_TOP/HOWTO/INSTALL.md</c></seealso>.</p>
+ <p>
+ Own Id: OTP-11742</p>
+ </item>
+ <item>
+ <p>
+ Halfword emulator is marked as deprecated. It still works
+ as before but is planned to be removed in a future major
+ release.</p>
+ <p>
+ Own Id: OTP-11777</p>
+ </item>
+ <item>
+ <p>
+ The external format for Maps has changed in a way that is
+ not compatible with the format used in OTP 17.0-rc1 and
+ OTP 17.0-rc2.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11782</p>
+ </item>
+ <item>
+ <p>
+ Fixed faulty make dependency that would make some make
+ versions fail while building gen_git_version.mk.</p>
+ <p>
+ Own Id: OTP-11784</p>
+ </item>
+ <item>
+ <p>
+ Introduced functionality for allowing old drivers and NIF
+ libraries to be loaded during a transition period. For
+ more information see <seealso
+ marker="erts:erl_driver#version_management">the version
+ management section in the <c>erl_driver(3)</c>
+ documentation</seealso> and <seealso
+ marker="erts:erl_nif#version_management">the version
+ management section in the <c>erl_nif(3)</c>
+ documentation</seealso>.</p>
+ <p>
+ Own Id: OTP-11799</p>
+ </item>
+ <item>
+ <p>
+ Support file paths longer than 259 characters on Windows.
+ Long absolute paths are automatically converted to UNC
+ format with a <c>\\?\</c> prefix which is the only way to
+ represent long paths. The 259 character limit still
+ applies for individual file names, relative paths and the
+ current working directory.</p>
+ <p>
+ Own Id: OTP-11813</p>
+ </item>
+ <item>
+ <p>
+ Document that escript:create/2 also accepts a 3-elements
+ tuple containing files and zip:create/3 options to build
+ a zip file.</p>
+ <p>
+ Thanks to Pierre Fenoll</p>
+ <p>
+ Own Id: OTP-11827</p>
+ </item>
+ <item>
+ <p>
+ Add systemd socket activation for epmd.</p>
+ <p>
+ Thanks to Matwey V. Kornilov</p>
+ <p>
+ Own Id: OTP-11829</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 5.10.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml
index d52cc7f3e2..7f7c28fc30 100644
--- a/erts/doc/src/time_correction.xml
+++ b/erts/doc/src/time_correction.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="utf8" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 523130d01a..7145824f91 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -378,7 +378,9 @@ LIBS += -l$(ERTS_INTERNAL_LIB)$(TYPEMARKER)
endif # erts_internal_r
+ifneq ($(TARGET),arm-unknown-linux-androideabi)
LIBS += @LIBRT@
+endif
LIBS += @LIBCARBON@
@@ -414,13 +416,6 @@ else
UNIX_ONLY_BUILDS =
endif
-ifeq ($(TARGET), win32)
-TMPVAR := $(shell LANG=C $(PERL) utils/make_compiler_flags -o $(TTF_DIR)/erl_compile_flags.h -v CONFIG_H "N/A" -v CFLAGS "$(CFLAGS)" -v LDFLAGS "$(LDFLAGS)")
-else
-# We force this to be run every time this makefile is executed
-TMPVAR := $(shell LANG=C $(PERL) utils/make_compiler_flags -o $(TTF_DIR)/erl_compile_flags.h -f CONFIG_H "$(ERL_TOP)/erts/$(TARGET)/config.h" -v CFLAGS "$(CFLAGS)" -v LDFLAGS "$(LDFLAGS)")
-endif
-
.PHONY: all
ifdef VOID_EMULATOR
all:
@@ -499,6 +494,15 @@ release_docs_spec:
_create_dirs := $(shell mkdir -p $(CREATE_DIRS))
+
+# has to be run after _create_dirs
+ifeq ($(TARGET), win32)
+TMPVAR := $(shell LANG=C $(PERL) utils/make_compiler_flags -o $(TTF_DIR)/erl_compile_flags.h -v CONFIG_H "N/A" -v CFLAGS "$(CFLAGS)" -v LDFLAGS "$(LDFLAGS)")
+else
+# We force this to be run every time this makefile is executed
+TMPVAR := $(shell LANG=C $(PERL) utils/make_compiler_flags -o $(TTF_DIR)/erl_compile_flags.h -f CONFIG_H "$(ERL_TOP)/erts/$(TARGET)/config.h" -v CFLAGS "$(CFLAGS)" -v LDFLAGS "$(LDFLAGS)")
+endif
+
GENERATE =
HIPE_ASM =
@@ -1031,11 +1035,10 @@ else
ifeq ($(findstring ose,$(TARGET)),ose)
$(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS) $(LCF)
$(call build-ose-load-module, $@, $(INIT_OBJS) $(OBJS), $(STATIC_NIF_LIBS) \
- $(STATIC_DRIVER_LIBS) $(LIBS), $(LMCONF))
+ $(STATIC_DRIVER_LIBS) $(LIBS), $(BEAM_LMCONF))
else
$(BINDIR)/$(EMULATOR_EXECUTABLE): $(INIT_OBJS) $(OBJS) $(DEPLIBS)
- echo $(DEPLIBS)
$(ld_verbose)$(PURIFY) $(LD) -o $(BINDIR)/$(EMULATOR_EXECUTABLE) \
$(HIPEBEAMLDFLAGS) $(LDFLAGS) $(DEXPORT) $(INIT_OBJS) $(OBJS) \
$(STATIC_NIF_LIBS) $(STATIC_DRIVER_LIBS) $(LIBS)
@@ -1087,7 +1090,9 @@ BEAM_SRC=$(wildcard beam/*.c)
DRV_COMMON_SRC=$(wildcard drivers/common/*.c)
DRV_OSTYPE_SRC=$(wildcard drivers/$(ERLANG_OSTYPE)/*.c)
ALL_SYS_SRC=$(wildcard sys/$(ERLANG_OSTYPE)/*.c) $(wildcard sys/common/*.c)
-TARGET_SRC=$(wildcard $(TARGET)/*.c) $(wildcard $(TTF_DIR)/*.c)
+# We use $(shell ls) here instead of wildcard as $(wildcard ) resolved at
+# loadtime of the makefile and at that time these files are not generated yet.
+TARGET_SRC=$(shell ls $(TARGET)/*.c) $(shell ls $(TTF_DIR)/*.c)
# I do not want the -MG flag on windows, it does not work properly for a
# windows build.
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index d28e519ae1..5d06a32941 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -116,6 +116,7 @@ atom binary_longest_prefix_trap
atom binary_longest_suffix_trap
atom binary_match_trap
atom binary_matches_trap
+atom binary_to_list_continue
atom binary_to_term_trap
atom block
atom blocked
@@ -315,6 +316,7 @@ atom line_length
atom linked_in_driver
atom links
atom list
+atom list_to_binary_continue
atom little
atom loaded
atom load_cancelled
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index 49a34ab4ad..4e711c89e0 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -642,7 +642,7 @@ erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg)
erts_smp_atomic_inc_nob(&bp->count->acount);
}
- if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE) {
+ if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE && erts_is_tracer_proc_valid(c_p)) {
Eterm w;
erts_trace_time_call(c_p, I, bp->time);
w = (BeamInstr) *c_p->cp;
@@ -730,7 +730,8 @@ erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
}
}
if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE &&
- IS_TRACED_FL(p, F_TRACE_CALLS)) {
+ IS_TRACED_FL(p, F_TRACE_CALLS) &&
+ erts_is_tracer_proc_valid(p)) {
BeamInstr *pc = (BeamInstr *)ep->code+3;
erts_trace_time_call(p, pc, bp->time);
}
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 0cec9ea3ec..1026e5f649 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -1301,7 +1301,7 @@ void process_main(void)
(Eterm)fptr[1], (Uint)fptr[2],
NULL, fun_buf);
} else {
- erts_snprintf(fun_buf, sizeof(fun_buf),
+ erts_snprintf(fun_buf, sizeof(DTRACE_CHARBUF_NAME(fun_buf)),
"<unknown/%p>", next);
}
}
@@ -3525,7 +3525,7 @@ get_map_elements_fail:
erts_post_nif(&env);
#ifdef ERTS_DIRTY_SCHEDULERS
if (is_non_value(nif_bif_result) && c_p->freason == TRAP) {
- Export* ep = (Export*) c_p->psd->data[ERTS_PSD_DIRTY_SCHED_TRAP_EXPORT];
+ Export* ep = ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(c_p);
ep->code[0] = I[-3];
ep->code[1] = I[-2];
}
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 06a1230ca0..fcbeb6cf5c 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -1886,8 +1886,13 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend, Eterm *refp) {
Eterm id = erts_whereis_name_to_id(p, to);
rp = erts_proc_lookup(id);
- if (rp)
+ if (rp) {
+ if (IS_TRACED(p))
+ trace_send(p, to, msg);
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
+ save_calls(p, &exp_send);
goto send_message;
+ }
pt = erts_port_lookup(id,
(erts_port_synchronous_ops
diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h
index 51b77a95ed..72c55ccb55 100644
--- a/erts/emulator/beam/bif.h
+++ b/erts/emulator/beam/bif.h
@@ -124,12 +124,85 @@ do { \
return THE_NON_VALUE; \
} while(0)
+#define ERTS_BIF_ERROR_TRAPPED0(Proc, Reason, Bif) \
+do { \
+ (Proc)->freason = (Reason); \
+ (Proc)->current = (Bif)->code; \
+ return THE_NON_VALUE; \
+} while (0)
+
+#define ERTS_BIF_ERROR_TRAPPED1(Proc, Reason, Bif, A0) \
+do { \
+ Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ (Proc)->freason = (Reason); \
+ (Proc)->current = (Bif)->code; \
+ reg[0] = (Eterm) (A0); \
+ return THE_NON_VALUE; \
+} while (0)
+
+#define ERTS_BIF_ERROR_TRAPPED2(Proc, Reason, Bif, A0, A1) \
+do { \
+ Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ (Proc)->freason = (Reason); \
+ (Proc)->current = (Bif)->code; \
+ reg[0] = (Eterm) (A0); \
+ reg[1] = (Eterm) (A1); \
+ return THE_NON_VALUE; \
+} while (0)
+
+#define ERTS_BIF_ERROR_TRAPPED3(Proc, Reason, Bif, A0, A1, A2) \
+do { \
+ Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ (Proc)->freason = (Reason); \
+ (Proc)->current = (Bif)->code; \
+ reg[0] = (Eterm) (A0); \
+ reg[1] = (Eterm) (A1); \
+ reg[2] = (Eterm) (A2); \
+ return THE_NON_VALUE; \
+} while (0)
+
#define ERTS_BIF_PREP_ERROR(Ret, Proc, Reason) \
do { \
(Proc)->freason = (Reason); \
(Ret) = THE_NON_VALUE; \
} while (0)
+#define ERTS_BIF_PREP_ERROR_TRAPPED0(Ret, Proc, Reason, Bif) \
+do { \
+ (Proc)->freason = (Reason); \
+ (Proc)->current = (Bif)->code; \
+ (Ret) = THE_NON_VALUE; \
+} while (0)
+
+#define ERTS_BIF_PREP_ERROR_TRAPPED1(Ret, Proc, Reason, Bif, A0) \
+do { \
+ Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ (Proc)->freason = (Reason); \
+ (Proc)->current = (Bif)->code; \
+ reg[0] = (Eterm) (A0); \
+ (Ret) = THE_NON_VALUE; \
+} while (0)
+
+#define ERTS_BIF_PREP_ERROR_TRAPPED2(Ret, Proc, Reason, Bif, A0, A1) \
+do { \
+ Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ (Proc)->freason = (Reason); \
+ (Proc)->current = (Bif)->code; \
+ reg[0] = (Eterm) (A0); \
+ reg[1] = (Eterm) (A1); \
+ (Ret) = THE_NON_VALUE; \
+} while (0)
+
+#define ERTS_BIF_PREP_ERROR_TRAPPED3(Ret, Proc, Reason, Bif, A0, A1, A2) \
+do { \
+ Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
+ (Proc)->freason = (Reason); \
+ (Proc)->current = (Bif)->code; \
+ reg[0] = (Eterm) (A0); \
+ reg[1] = (Eterm) (A1); \
+ reg[2] = (Eterm) (A2); \
+ (Ret) = THE_NON_VALUE; \
+} while (0)
#define ERTS_BIF_PREP_TRAP0(Ret, Trap, Proc) \
do { \
@@ -392,6 +465,51 @@ erts_bif_prep_await_proc_exit_apply_trap(Process *c_p,
Eterm args[],
int nargs);
+#ifndef HIPE
+
+#define HIPE_WRAPPER_BIF_DISABLE_GC(BIF_NAME, ARITY)
+
+#else
+
+#include "erl_fun.h"
+#include "hipe_mode_switch.h"
+
+/*
+ * Hipe wrappers used by native code for BIFs that disable GC while trapping.
+ * Also add usage of the wrapper in ../hipe/hipe_bif_list.m4
+ *
+ * Problem:
+ * When native code calls a BIF that traps, hipe_mode_switch will push a
+ * "trap frame" on the Erlang stack in order to find its way back from beam_emu
+ * back to native caller when finally done. If GC is disabled and stack/heap
+ * is full there is no place to push the "trap frame".
+ *
+ * Solution:
+ * We reserve space on stack for the "trap frame" here before the BIF is called.
+ * If the BIF does not trap, the space is reclaimed here before returning.
+ * If the BIF traps, hipe_push_beam_trap_frame() will detect that a "trap frame"
+ * already is reserved and use it.
+ */
+
+
+#define HIPE_WRAPPER_BIF_DISABLE_GC(BIF_NAME, ARITY) \
+BIF_RETTYPE hipe_wrapper_ ## BIF_NAME ## _ ## ARITY (Process* c_p, \
+ Eterm* args); \
+BIF_RETTYPE hipe_wrapper_ ## BIF_NAME ## _ ## ARITY (Process* c_p, \
+ Eterm* args) \
+{ \
+ BIF_RETTYPE res; \
+ hipe_reserve_beam_trap_frame(c_p, args, ARITY); \
+ res = BIF_NAME ## _ ## ARITY (c_p, args); \
+ if (is_value(res) || c_p->freason != TRAP) { \
+ hipe_unreserve_beam_trap_frame(c_p); \
+ } \
+ return res; \
+}
+
+#endif
+
+
#include "erl_bif_table.h"
#endif
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 2d888862bf..011e49f1fe 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -45,6 +45,7 @@ bif erlang:apply/3
bif erlang:atom_to_list/1
bif erlang:binary_to_list/1
bif erlang:binary_to_list/3
+bif erlang:binary_to_term/1
bif erlang:crc32/1
bif erlang:crc32/2
bif erlang:crc32_combine/3
@@ -151,12 +152,11 @@ bif erts_internal:port_command/3
bif erts_internal:port_control/3
bif erts_internal:port_close/1
bif erts_internal:port_connect/2
-bif erts_internal:binary_to_term/1
-bif erts_internal:binary_to_term/2
bif erts_internal:request_system_task/3
bif erts_internal:check_process_code/2
+bif erts_internal:map_to_tuple_keys/1
# inet_db support
bif erlang:port_set_data/2
@@ -480,6 +480,11 @@ bif erlang:call_on_load_function/1
bif erlang:finish_after_on_load/2
#
+# New Bifs in R13B04
+#
+bif erlang:binary_to_term/2
+
+#
# The binary match bifs (New in R14A - EEP9)
#
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index c7926f18af..f50d484576 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -31,12 +31,11 @@
#include "erl_binary.h"
#include "erl_bits.h"
-#ifdef DEBUG
-static int list_to_bitstr_buf(Eterm obj, char* buf, Uint len);
-#else
-static int list_to_bitstr_buf(Eterm obj, char* buf);
-#endif
-static int bitstr_list_len(Eterm obj, Uint* num_bytes);
+static Export binary_to_list_continue_export;
+static Export list_to_binary_continue_export;
+
+static BIF_RETTYPE binary_to_list_continue(BIF_ALIST_1);
+static BIF_RETTYPE list_to_binary_continue(BIF_ALIST_1);
void
erts_init_binary(void)
@@ -49,6 +48,15 @@ erts_init_binary(void)
"Internal error: Address of orig_bytes[0] of a Binary"
" is *not* 8-byte aligned\n");
}
+
+ erts_init_trap_export(&binary_to_list_continue_export,
+ am_erts_internal, am_binary_to_list_continue, 1,
+ &binary_to_list_continue);
+
+ erts_init_trap_export(&list_to_binary_continue_export,
+ am_erts_internal, am_list_to_binary_continue, 1,
+ &list_to_binary_continue);
+
}
/*
@@ -333,6 +341,132 @@ BIF_RETTYPE integer_to_binary_1(BIF_ALIST_1)
BIF_RET(res);
}
+#define ERTS_B2L_BYTES_PER_REDUCTION 256
+
+typedef struct {
+ Eterm res;
+ Eterm *hp;
+#ifdef DEBUG
+ Eterm *hp_end;
+#endif
+ byte *bytes;
+ Uint size;
+ Uint bitoffs;
+} ErtsB2LState;
+
+static void b2l_state_destructor(Binary *mbp)
+{
+ ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(mbp) == b2l_state_destructor);
+}
+
+static BIF_RETTYPE
+binary_to_list_chunk(Process *c_p,
+ Eterm mb_eterm,
+ ErtsB2LState* sp,
+ int reds_left,
+ int gc_disabled)
+{
+ BIF_RETTYPE ret;
+ int bump_reds;
+ Uint size;
+ byte *bytes;
+
+ size = (reds_left + 1)*ERTS_B2L_BYTES_PER_REDUCTION;
+ if (size > sp->size)
+ size = sp->size;
+ bytes = sp->bytes + (sp->size - size);
+
+ bump_reds = (size - 1)/ERTS_B2L_BYTES_PER_REDUCTION + 1;
+ BUMP_REDS(c_p, bump_reds);
+
+ ASSERT(is_list(sp->res) || is_nil(sp->res));
+
+ sp->res = erts_bin_bytes_to_list(sp->res,
+ sp->hp,
+ bytes,
+ size,
+ sp->bitoffs);
+ sp->size -= size;
+ sp->hp += 2*size;
+
+ if (sp->size > 0) {
+
+ if (!gc_disabled)
+ erts_set_gc_state(c_p, 0);
+
+ ASSERT(c_p->flags & F_DISABLE_GC);
+ ASSERT(is_value(mb_eterm));
+ ERTS_BIF_PREP_TRAP1(ret,
+ &binary_to_list_continue_export,
+ c_p,
+ mb_eterm);
+ }
+ else {
+
+ ASSERT(sp->hp == sp->hp_end);
+ ASSERT(sp->size == 0);
+
+ if (!gc_disabled || !erts_set_gc_state(c_p, 1))
+ ERTS_BIF_PREP_RET(ret, sp->res);
+ else
+ ERTS_BIF_PREP_YIELD_RETURN(ret, c_p, sp->res);
+ ASSERT(!(c_p->flags & F_DISABLE_GC));
+ }
+
+ return ret;
+}
+
+static ERTS_INLINE BIF_RETTYPE
+binary_to_list(Process *c_p, Eterm *hp, Eterm tail, byte *bytes, Uint size, Uint bitoffs)
+{
+ int reds_left = ERTS_BIF_REDS_LEFT(c_p);
+ if (size < reds_left*ERTS_B2L_BYTES_PER_REDUCTION) {
+ Eterm res;
+ BIF_RETTYPE ret;
+ int bump_reds = (size - 1)/ERTS_B2L_BYTES_PER_REDUCTION + 1;
+ BUMP_REDS(c_p, bump_reds);
+ res = erts_bin_bytes_to_list(tail, hp, bytes, size, bitoffs);
+ ERTS_BIF_PREP_RET(ret, res);
+ return ret;
+ }
+ else {
+ Binary *mbp = erts_create_magic_binary(sizeof(ErtsB2LState),
+ b2l_state_destructor);
+ ErtsB2LState *sp = ERTS_MAGIC_BIN_DATA(mbp);
+ Eterm mb;
+
+ sp->res = tail;
+ sp->hp = hp;
+#ifdef DEBUG
+ sp->hp_end = sp->hp + 2*size;
+#endif
+ sp->bytes = bytes;
+ sp->size = size;
+ sp->bitoffs = bitoffs;
+
+ hp = HAlloc(c_p, PROC_BIN_SIZE);
+ mb = erts_mk_magic_binary_term(&hp, &MSO(c_p), mbp);
+ return binary_to_list_chunk(c_p, mb, sp, reds_left, 0);
+ }
+}
+
+static BIF_RETTYPE binary_to_list_continue(BIF_ALIST_1)
+{
+ Binary *mbp = ((ProcBin *) binary_val(BIF_ARG_1))->val;
+
+ ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(mbp) == b2l_state_destructor);
+
+ ASSERT(BIF_P->flags & F_DISABLE_GC);
+
+ return binary_to_list_chunk(BIF_P,
+ BIF_ARG_1,
+ (ErtsB2LState*) ERTS_MAGIC_BIN_DATA(mbp),
+ ERTS_BIF_REDS_LEFT(BIF_P),
+ 1);
+}
+
+HIPE_WRAPPER_BIF_DISABLE_GC(binary_to_list, 1)
+
BIF_RETTYPE binary_to_list_1(BIF_ALIST_1)
{
Eterm real_bin;
@@ -354,14 +488,15 @@ BIF_RETTYPE binary_to_list_1(BIF_ALIST_1)
} else {
Eterm* hp = HAlloc(BIF_P, 2 * size);
byte* bytes = binary_bytes(real_bin)+offset;
-
- BIF_RET(erts_bin_bytes_to_list(NIL, hp, bytes, size, bitoffs));
+ return binary_to_list(BIF_P, hp, NIL, bytes, size, bitoffs);
}
error:
BIF_ERROR(BIF_P, BADARG);
}
+HIPE_WRAPPER_BIF_DISABLE_GC(binary_to_list, 3)
+
BIF_RETTYPE binary_to_list_3(BIF_ALIST_3)
{
byte* bytes;
@@ -387,12 +522,13 @@ BIF_RETTYPE binary_to_list_3(BIF_ALIST_3)
}
i = stop-start+1;
hp = HAlloc(BIF_P, 2*i);
- BIF_RET(erts_bin_bytes_to_list(NIL, hp, bytes+start-1, i, bitoffs));
-
+ return binary_to_list(BIF_P, hp, NIL, bytes+start-1, i, bitoffs);
error:
BIF_ERROR(BIF_P, BADARG);
}
+HIPE_WRAPPER_BIF_DISABLE_GC(bitstring_to_list, 1)
+
BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1)
{
Eterm real_bin;
@@ -431,124 +567,441 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1)
previous = CONS(hp, make_binary(last), previous);
hp += 2;
}
- BIF_RET(erts_bin_bytes_to_list(previous, hp, bytes, size, bitoffs));
+
+ return binary_to_list(BIF_P, hp, previous, bytes, size, bitoffs);
}
/* Turn a possibly deep list of ints (and binaries) into */
/* One large binary object */
-/*
- * This bif also exists in the binary module, under the name
- * binary:list_to_bin/1, why it's divided into interface and
- * implementation. Also the backend for iolist_to_binary_1.
- */
+typedef enum {
+ ERTS_L2B_OK,
+ ERTS_L2B_YIELD,
+ ERTS_L2B_TYPE_ERROR,
+ ERTS_L2B_OVERFLOW_ERROR
+} ErtsL2BResult;
-BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg)
-{
+#define ERTS_L2B_STATE_INITER(C_P, ARG, BIF, SZFunc, TBufFunc) \
+ {ERTS_IOLIST2BUF_STATE_INITER((C_P), (ARG)), \
+ (ARG), THE_NON_VALUE, (BIF), (SZFunc), (TBufFunc)}
+
+#define ERTS_L2B_STATE_MOVE(TO, FROM) \
+ sys_memcpy((void *) (TO), (void *) (FROM), sizeof(ErtsL2BState))
+
+typedef struct ErtsL2BState_ ErtsL2BState;
+
+struct ErtsL2BState_ {
+ ErtsIOList2BufState buf;
+ Eterm arg;
Eterm bin;
- Eterm h,t;
- ErlDrvSizeT size;
- byte* bytes;
-#ifdef DEBUG
- ErlDrvSizeT offset;
-#endif
+ Export *bif;
+ int (*iolist_to_buf_size)(ErtsIOListState *);
+ ErlDrvSizeT (*iolist_to_buf)(ErtsIOList2BufState *);
+};
+
+static ERTS_INLINE ErtsL2BResult
+list_to_binary_engine(ErtsL2BState *sp)
+{
+ ErlDrvSizeT res;
+ Process *c_p = sp->buf.iolist.c_p;
+
+ /*
+ * have_size == 0 while sp->iolist_to_buf_size()
+ * has not finished the calculation.
+ */
+
+ if (!sp->buf.iolist.have_size) {
+ switch (sp->iolist_to_buf_size(&sp->buf.iolist)) {
+ case ERTS_IOLIST_YIELD:
+ return ERTS_L2B_YIELD;
+ case ERTS_IOLIST_OVERFLOW:
+ return ERTS_L2B_OVERFLOW_ERROR;
+ case ERTS_IOLIST_TYPE:
+ return ERTS_L2B_TYPE_ERROR;
+ case ERTS_IOLIST_OK:
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ ASSERT(sp->buf.iolist.have_size);
+
+ /*
+ * Size calculated... Setup state for
+ * sp->iolist_to_buf_*()
+ */
+
+ sp->bin = new_binary(c_p,
+ (byte *) NULL,
+ sp->buf.iolist.size);
+
+ if (sp->buf.iolist.size == 0)
+ return ERTS_L2B_OK;
+
+ sp->buf.buf = (char *) binary_bytes(sp->bin);
+ sp->buf.len = sp->buf.iolist.size;
+ sp->buf.iolist.obj = sp->arg;
- if (is_nil(arg)) {
- BIF_RET(new_binary(p,(byte*)"",0));
+ if (sp->buf.iolist.reds_left <= 0) {
+ BUMP_ALL_REDS(c_p);
+ return ERTS_L2B_YIELD;
+ }
}
- if (is_not_list(arg)) {
- goto error;
+
+ ASSERT(sp->buf.iolist.size != 0);
+ ASSERT(is_value(sp->bin));
+ ASSERT(sp->buf.buf);
+
+ res = sp->iolist_to_buf(&sp->buf);
+
+ if (!ERTS_IOLIST_TO_BUF_FAILED(res)) {
+ ASSERT(res == 0);
+ return ERTS_L2B_OK;
}
- /* check for [binary()] case */
- h = CAR(list_val(arg));
- t = CDR(list_val(arg));
- if (is_binary(h) && is_nil(t) && !(
- HEADER_SUB_BIN == *(binary_val(h)) && (
- ((ErlSubBin *)binary_val(h))->bitoffs != 0 ||
- ((ErlSubBin *)binary_val(h))->bitsize != 0
- ))) {
- return h;
- }
- switch (erts_iolist_size(arg, &size)) {
- case ERTS_IOLIST_OVERFLOW: BIF_ERROR(p, SYSTEM_LIMIT);
- case ERTS_IOLIST_TYPE: goto error;
- default: ;
- }
- bin = new_binary(p, (byte *)NULL, size);
- bytes = binary_bytes(bin);
-#ifdef DEBUG
- offset =
-#endif
- erts_iolist_to_buf(arg, (char*) bytes, size);
- ASSERT(offset == 0);
- BIF_RET(bin);
+ switch (res) {
+ case ERTS_IOLIST_TO_BUF_YIELD:
+ return ERTS_L2B_YIELD;
+ case ERTS_IOLIST_TO_BUF_OVERFLOW:
+ return ERTS_L2B_OVERFLOW_ERROR;
+ case ERTS_IOLIST_TO_BUF_TYPE_ERROR:
+ return ERTS_L2B_TYPE_ERROR;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid return value from iolist_to_buf_yielding()");
+ return ERTS_L2B_TYPE_ERROR;
+ }
+}
+
+static void
+l2b_state_destructor(Binary *mbp)
+{
+ ErtsL2BState *sp = ERTS_MAGIC_BIN_DATA(mbp);
+ ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(mbp) == l2b_state_destructor);
+ DESTROY_SAVED_ESTACK(&sp->buf.iolist.estack);
+}
+
+static ERTS_INLINE Eterm
+l2b_final_touch(Process *c_p, ErtsL2BState *sp)
+{
+ Eterm *hp;
+ ErlSubBin* sbin;
+ if (sp->buf.offset == 0)
+ return sp->bin;
+
+ hp = HAlloc(c_p, ERL_SUB_BIN_SIZE);
+ ASSERT(sp->buf.offset > 0);
+ sbin = (ErlSubBin *) hp;
+ sbin->thing_word = HEADER_SUB_BIN;
+ sbin->size = sp->buf.iolist.size-1;
+ sbin->offs = 0;
+ sbin->orig = sp->bin;
+ sbin->bitoffs = 0;
+ sbin->bitsize = sp->buf.offset;
+ sbin->is_writable = 0;
+ return make_binary(sbin);
+}
+
+static BIF_RETTYPE
+list_to_binary_chunk(Eterm mb_eterm,
+ ErtsL2BState* sp,
+ int reds_left,
+ int gc_disabled)
+{
+ Eterm err = BADARG;
+ BIF_RETTYPE ret;
+ Process *c_p = sp->buf.iolist.c_p;
+
+ sp->buf.iolist.reds_left = reds_left;
- error:
- BIF_ERROR(p, BADARG);
+ switch (list_to_binary_engine(sp)) {
+
+ case ERTS_L2B_OK: {
+ Eterm result = l2b_final_touch(c_p, sp);
+ if (!gc_disabled || !erts_set_gc_state(c_p, 1))
+ ERTS_BIF_PREP_RET(ret, result);
+ else
+ ERTS_BIF_PREP_YIELD_RETURN(ret, c_p, result);
+ ASSERT(!(c_p->flags & F_DISABLE_GC));
+ break;
+ }
+ case ERTS_L2B_YIELD:
+ if (!gc_disabled) {
+ /* first yield... */
+ Eterm *hp;
+ Binary *mbp = erts_create_magic_binary(sizeof(ErtsL2BState),
+ l2b_state_destructor);
+ ErtsL2BState *new_sp = ERTS_MAGIC_BIN_DATA(mbp);
+
+ ERTS_L2B_STATE_MOVE(new_sp, sp);
+ sp = new_sp;
+
+ hp = HAlloc(c_p, PROC_BIN_SIZE);
+ mb_eterm = erts_mk_magic_binary_term(&hp, &MSO(c_p), mbp);
+
+ ASSERT(is_value(mb_eterm));
+
+ erts_set_gc_state(c_p, 0);
+ }
+
+ ASSERT(c_p->flags & F_DISABLE_GC);
+
+ ERTS_BIF_PREP_TRAP1(ret,
+ &list_to_binary_continue_export,
+ c_p,
+ mb_eterm);
+ break;
+
+ case ERTS_L2B_OVERFLOW_ERROR:
+ err = SYSTEM_LIMIT;
+ /* fall through */
+
+ case ERTS_L2B_TYPE_ERROR:
+ if (!gc_disabled)
+ ERTS_BIF_PREP_ERROR(ret, c_p, err);
+ else {
+ if (erts_set_gc_state(c_p, 1))
+ ERTS_VBUMP_ALL_REDS(c_p);
+
+ ERTS_BIF_PREP_ERROR_TRAPPED1(ret,
+ c_p,
+ err,
+ sp->bif,
+ sp->arg);
+ }
+
+ ASSERT(!(c_p->flags & F_DISABLE_GC));
+ break;
+
+ default:
+ ERTS_INTERNAL_ERROR("Invalid return value from list_to_binary_engine()");
+ ERTS_BIF_PREP_ERROR(ret,c_p, EXC_INTERNAL_ERROR);
+ break;
+ }
+ return ret;
}
+static BIF_RETTYPE list_to_binary_continue(BIF_ALIST_1)
+{
+ Binary *mbp = ((ProcBin *) binary_val(BIF_ARG_1))->val;
+ ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(mbp) == l2b_state_destructor);
+
+ ASSERT(BIF_P->flags & F_DISABLE_GC);
+
+ return list_to_binary_chunk(BIF_ARG_1,
+ ERTS_MAGIC_BIN_DATA(mbp),
+ ERTS_BIF_REDS_LEFT(BIF_P),
+ 1);
+}
+
+BIF_RETTYPE erts_list_to_binary_bif(Process *c_p, Eterm arg, Export *bif)
+{
+ BIF_RETTYPE ret;
+
+ if (is_nil(arg))
+ ERTS_BIF_PREP_RET(ret, new_binary(c_p, (byte *) "", 0));
+ else if (is_not_list(arg))
+ ERTS_BIF_PREP_ERROR(ret, c_p, BADARG);
+ else {
+ /* check for [binary()] case */
+ Eterm h = CAR(list_val(arg));
+ Eterm t = CDR(list_val(arg));
+ if (is_binary(h)
+ && is_nil(t)
+ && !(HEADER_SUB_BIN == *(binary_val(h))
+ && (((ErlSubBin *)binary_val(h))->bitoffs != 0
+ || ((ErlSubBin *)binary_val(h))->bitsize != 0))) {
+ ERTS_BIF_PREP_RET(ret, h);
+ }
+ else {
+ ErtsL2BState state = ERTS_L2B_STATE_INITER(c_p,
+ arg,
+ bif,
+ erts_iolist_size_yielding,
+ erts_iolist_to_buf_yielding);
+ int orig_reds_left = ERTS_BIF_REDS_LEFT(c_p);
+
+ /*
+ * First try to do it all at once without having to use
+ * yielding iolist_to_buf().
+ */
+ state.buf.iolist.reds_left = orig_reds_left;
+ switch (erts_iolist_size_yielding(&state.buf.iolist)) {
+ case ERTS_IOLIST_OK: {
+ ErlDrvSizeT size = state.buf.iolist.size;
+ Eterm bin;
+ char *buf;
+
+ if (size == 0) {
+ ERTS_BIF_PREP_RET(ret, new_binary(c_p, (byte *) NULL, 0));
+ break; /* done */
+ }
+
+ bin = new_binary(c_p, (byte *) NULL, size);
+ buf = (char *) binary_bytes(bin);
+
+ if (size < ERTS_IOLIST_TO_BUF_BYTES_PER_RED*CONTEXT_REDS) {
+ /* An (over) estimation of reductions needed */
+ int reds_left = state.buf.iolist.reds_left;
+ int to_buf_reds = orig_reds_left - reds_left;
+ to_buf_reds += size/ERTS_IOLIST_TO_BUF_BYTES_PER_RED;
+ if (to_buf_reds <= reds_left) {
+ ErlDrvSizeT res;
+
+ res = erts_iolist_to_buf(arg, buf, size);
+ if (res == 0) {
+ BUMP_REDS(c_p, to_buf_reds);
+ ERTS_BIF_PREP_RET(ret, bin);
+ break; /* done */
+ }
+ if (!ERTS_IOLIST_TO_BUF_FAILED(res))
+ ERTS_INTERNAL_ERROR("iolist_size/iolist_to_buf missmatch");
+ if (res == ERTS_IOLIST_TO_BUF_OVERFLOW)
+ goto overflow;
+ goto type_error;
+ }
+ }
+ /*
+ * Since size has been computed list_to_binary_chunk() expects
+ * state prepared for iolist_to_buf.
+ */
+ state.bin = bin;
+ state.buf.buf = buf;
+ state.buf.len = size;
+ state.buf.iolist.obj = arg;
+ /* Fall through... */
+ }
+ case ERTS_IOLIST_YIELD:
+ ret = list_to_binary_chunk(THE_NON_VALUE,
+ &state,
+ state.buf.iolist.reds_left,
+ 0);
+ break;
+ case ERTS_IOLIST_OVERFLOW:
+ overflow:
+ ERTS_BIF_PREP_ERROR(ret, c_p, SYSTEM_LIMIT);
+ break;
+ case ERTS_IOLIST_TYPE:
+ type_error:
+ default:
+ ERTS_BIF_PREP_ERROR(ret, c_p, BADARG);
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+HIPE_WRAPPER_BIF_DISABLE_GC(list_to_binary, 1)
+
BIF_RETTYPE list_to_binary_1(BIF_ALIST_1)
{
- return erts_list_to_binary_bif(BIF_P, BIF_ARG_1);
+ return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_list_to_binary_1]);
}
-/* Turn a possibly deep list of ints (and binaries) into */
-/* One large binary object */
+HIPE_WRAPPER_BIF_DISABLE_GC(iolist_to_binary, 1)
BIF_RETTYPE iolist_to_binary_1(BIF_ALIST_1)
{
if (is_binary(BIF_ARG_1)) {
BIF_RET(BIF_ARG_1);
}
- return erts_list_to_binary_bif(BIF_P, BIF_ARG_1);
+ return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_iolist_to_binary_1]);
}
+static int bitstr_list_len(ErtsIOListState *);
+static ErlDrvSizeT list_to_bitstr_buf_yielding(ErtsIOList2BufState *);
+static ErlDrvSizeT list_to_bitstr_buf_not_yielding(ErtsIOList2BufState *);
+
+HIPE_WRAPPER_BIF_DISABLE_GC(list_to_bitstring, 1)
+
BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1)
{
- Eterm bin;
- Uint sz;
- int offset;
- byte* bytes;
- ErlSubBin* sb1;
- Eterm* hp;
-
- if (is_nil(BIF_ARG_1)) {
- BIF_RET(new_binary(BIF_P,(byte*)"",0));
- }
- if (is_not_list(BIF_ARG_1)) {
- error:
- BIF_ERROR(BIF_P, BADARG);
- }
- switch (bitstr_list_len(BIF_ARG_1, &sz)) {
- case ERTS_IOLIST_TYPE:
- goto error;
- case ERTS_IOLIST_OVERFLOW:
- BIF_ERROR(BIF_P, SYSTEM_LIMIT);
- }
- bin = new_binary(BIF_P, (byte *)NULL, sz);
- bytes = binary_bytes(bin);
-#ifdef DEBUG
- offset = list_to_bitstr_buf(BIF_ARG_1, (char*) bytes, sz);
-#else
- offset = list_to_bitstr_buf(BIF_ARG_1, (char*) bytes);
-#endif
- ASSERT(offset >= 0);
- if (offset > 0) {
- hp = HAlloc(BIF_P, ERL_SUB_BIN_SIZE);
- sb1 = (ErlSubBin *) hp;
- sb1->thing_word = HEADER_SUB_BIN;
- sb1->size = sz-1;
- sb1->offs = 0;
- sb1->orig = bin;
- sb1->bitoffs = 0;
- sb1->bitsize = offset;
- sb1->is_writable = 0;
- bin = make_binary(sb1);
+ BIF_RETTYPE ret;
+
+ if (is_nil(BIF_ARG_1))
+ ERTS_BIF_PREP_RET(ret, new_binary(BIF_P, (byte *) "", 0));
+ else if (is_not_list(BIF_ARG_1))
+ ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
+ else {
+ /* check for [bitstring()] case */
+ Eterm h = CAR(list_val(BIF_ARG_1));
+ Eterm t = CDR(list_val(BIF_ARG_1));
+ if (is_binary(h) && is_nil(t)) {
+ ERTS_BIF_PREP_RET(ret, h);
+ }
+ else {
+ ErtsL2BState state = ERTS_L2B_STATE_INITER(BIF_P,
+ BIF_ARG_1,
+ bif_export[BIF_list_to_bitstring_1],
+ bitstr_list_len,
+ list_to_bitstr_buf_yielding);
+ int orig_reds_left = ERTS_BIF_REDS_LEFT(BIF_P);
+
+ /*
+ * First try to do it all at once without having to use
+ * yielding list_to_bitstr_buf().
+ */
+ state.buf.iolist.reds_left = orig_reds_left;
+ switch (bitstr_list_len(&state.buf.iolist)) {
+ case ERTS_IOLIST_OK: {
+ ErlDrvSizeT size = state.buf.iolist.size;
+
+ state.bin = new_binary(BIF_P, (byte *) NULL, size);
+ state.buf.buf = (char *) binary_bytes(state.bin);
+ state.buf.len = size;
+ state.buf.iolist.obj = BIF_ARG_1;
+
+ if (size < ERTS_IOLIST_TO_BUF_BYTES_PER_RED*CONTEXT_REDS) {
+ /* An (over) estimation of reductions needed */
+ int reds_left = state.buf.iolist.reds_left;
+ int to_buf_reds = orig_reds_left - reds_left;
+ to_buf_reds += size/ERTS_IOLIST_TO_BUF_BYTES_PER_RED;
+ if (to_buf_reds <= reds_left) {
+ ErlDrvSizeT res;
+
+ res = list_to_bitstr_buf_not_yielding(&state.buf);
+ if (res == 0) {
+ Eterm res_bin = l2b_final_touch(BIF_P, &state);
+ BUMP_REDS(BIF_P, to_buf_reds);
+ ERTS_BIF_PREP_RET(ret, res_bin);
+ break; /* done */
+ }
+ if (!ERTS_IOLIST_TO_BUF_FAILED(res))
+ ERTS_INTERNAL_ERROR("iolist_size/iolist_to_buf missmatch");
+ if (res == ERTS_IOLIST_TO_BUF_OVERFLOW)
+ goto overflow;
+ goto type_error;
+ }
+ }
+ /*
+ * Since size has been computed list_to_binary_chunk() expects
+ * the state prepared for list_to_bitstr_buf.
+ */
+
+ /* Fall through... */
+ }
+ case ERTS_IOLIST_YIELD:
+ ret = list_to_binary_chunk(THE_NON_VALUE,
+ &state,
+ state.buf.iolist.reds_left,
+ 0);
+ break;
+ case ERTS_IOLIST_OVERFLOW:
+ overflow:
+ ERTS_BIF_PREP_ERROR(ret, BIF_P, SYSTEM_LIMIT);
+ break;
+ case ERTS_IOLIST_TYPE:
+ type_error:
+ default:
+ ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
+ break;
+ }
+ }
}
-
- BIF_RET(bin);
+
+ return ret;
}
BIF_RETTYPE split_binary_2(BIF_ALIST_2)
@@ -605,123 +1058,353 @@ BIF_RETTYPE split_binary_2(BIF_ALIST_2)
* Local functions.
*/
+static int
+list_to_bitstr_buf_bcopy(ErtsIOList2BufState *state, Eterm obj, int *yield_countp);
+
/*
* The input list is assumed to be type-correct and the buffer is
* assumed to be of sufficient size. Those assumptions are verified in
* the DEBUG-built emulator.
*/
-static int
+static ErlDrvSizeT
+list_to_bitstr_buf(int yield_support, ErtsIOList2BufState *state)
+{
+
+#undef LIST_TO_BITSTR_BUF_BCOPY_DBG
+#undef LIST_TO_BITSTR_BUF_BCOPY
#ifdef DEBUG
-list_to_bitstr_buf(Eterm obj, char* buf, Uint len)
+#define LIST_TO_BITSTR_BUF_BCOPY_DBG \
+ len -= size + (offset>7);
#else
-list_to_bitstr_buf(Eterm obj, char* buf)
+#define LIST_TO_BITSTR_BUF_BCOPY_DBG
#endif
-{
- Eterm* objp;
- int offset = 0;
+#define LIST_TO_BITSTR_BUF_BCOPY(CONSP) \
+ do { \
+ byte* bptr; \
+ Uint bitsize; \
+ Uint bitoffs; \
+ Uint num_bits; \
+ size_t size = binary_size(obj); \
+ if (yield_support) { \
+ size_t max_size = ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; \
+ if (yield_count > 0) \
+ max_size *= yield_count+1; \
+ if (size > max_size) { \
+ state->objp = CONSP; \
+ goto L_bcopy_yield; \
+ } \
+ if (size >= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT) { \
+ int cost = (int) size; \
+ cost /= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; \
+ yield_count -= cost; \
+ } \
+ } \
+ ASSERT(size <= len); \
+ ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); \
+ num_bits = 8*size+bitsize; \
+ copy_binary_to_buffer(buf, offset, bptr, bitoffs, num_bits); \
+ offset += bitsize; \
+ buf += size + (offset>7); \
+ LIST_TO_BITSTR_BUF_BCOPY_DBG; \
+ offset = offset & 7; \
+ } while(0)
+
+#ifdef DEBUG
+ ErlDrvSizeT len;
+#endif
+ Eterm obj;
+ char *buf;
+ Eterm *objp = NULL;
+ int offset;
+ int init_yield_count = 0, yield_count;
DECLARE_ESTACK(s);
- goto L_again;
-
- while (!ESTACK_ISEMPTY(s)) {
- obj = ESTACK_POP(s);
- L_again:
- if (is_list(obj)) {
- L_iter_list:
- objp = list_val(obj);
- obj = CAR(objp);
- if (is_byte(obj)) {
- ASSERT(len > 0);
- if (offset == 0) {
- *buf++ = unsigned_val(obj);
- } else {
- *buf = (char)((unsigned_val(obj) >> offset) |
- ((*buf >> (8-offset)) << (8-offset)));
- buf++;
- *buf = (unsigned_val(obj) << (8-offset));
- }
+
+ obj = state->iolist.obj;
+ buf = state->buf;
+ offset = state->offset;
#ifdef DEBUG
- len--;
+ len = state->len;
#endif
- } else if (is_binary(obj)) {
- byte* bptr;
- size_t size = binary_size(obj);
- Uint bitsize;
- Uint bitoffs;
- Uint num_bits;
-
- ASSERT(size <= len);
- ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize);
- num_bits = 8*size+bitsize;
- copy_binary_to_buffer(buf, offset, bptr, bitoffs, num_bits);
- offset += bitsize;
- buf += size + (offset>7);
+
+ if (!yield_support) {
+ yield_count = init_yield_count = 0; /* Shut up faulty warning... >:-( */
+ goto L_again;
+ }
+ else {
+
+ if (state->iolist.reds_left <= 0)
+ return ERTS_IOLIST_TO_BUF_YIELD;
+
+ ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
+ init_yield_count = (ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED
+ * state->iolist.reds_left);
+ yield_count = init_yield_count;
+
+ if (!state->iolist.estack.start)
+ goto L_again;
+ else {
+ int chk_stack;
+ /* Restart; restore state... */
+ ESTACK_RESTORE(s, &state->iolist.estack);
+
+ if (!state->bcopy.bptr)
+ chk_stack = 0;
+ else {
+ chk_stack = 1;
+ if (list_to_bitstr_buf_bcopy(state, THE_NON_VALUE, &yield_count)) {
+ /* Yield again... */
+ BUMP_ALL_REDS(state->iolist.c_p);
+ state->iolist.reds_left = 0;
+ ESTACK_SAVE(s, &state->iolist.estack);
+ return ERTS_IOLIST_TO_BUF_YIELD;
+ }
+ buf = state->buf;
+ offset = state->offset;
#ifdef DEBUG
- len -= size + (offset>7);
+ len = state->len;
#endif
- offset = offset & 7;
- } else if (is_list(obj)) {
- ESTACK_PUSH(s, CDR(objp));
- goto L_iter_list; /* on head */
- } else {
- ASSERT(is_nil(obj));
}
- obj = CDR(objp);
- if (is_list(obj)) {
- goto L_iter_list; /* on tail */
- } else if (is_binary(obj)) {
- byte* bptr;
- size_t size = binary_size(obj);
- Uint bitsize;
- Uint bitoffs;
- Uint num_bits;
-
- ASSERT(size <= len);
- ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize);
- num_bits = 8*size+bitsize;
- copy_binary_to_buffer(buf, offset, bptr, bitoffs, num_bits);
- offset += bitsize;
- buf += size+(offset>7);
+ objp = state->objp;
+ state->objp = NULL;
+
+ if (objp)
+ goto L_tail;
+ if (!chk_stack)
+ goto L_again;
+ /* check stack */
+ }
+ }
+
+ while (!ESTACK_ISEMPTY(s)) {
+ obj = ESTACK_POP(s);
+ L_again:
+ if (is_list(obj)) {
+ while (1) { /* Tail loop */
+ while (1) { /* Head loop */
+ if (yield_support && --yield_count <= 0)
+ goto L_yield;
+ objp = list_val(obj);
+ obj = CAR(objp);
+ if (is_byte(obj)) {
+ ASSERT(len > 0);
+ if (offset == 0) {
+ *buf++ = unsigned_val(obj);
+ } else {
+ *buf = (char)((unsigned_val(obj) >> offset) |
+ ((*buf >> (8-offset)) << (8-offset)));
+ buf++;
+ *buf = (unsigned_val(obj) << (8-offset));
+ }
#ifdef DEBUG
- len -= size+(offset>7);
+ len--;
#endif
- offset = offset & 7;
- } else {
- ASSERT(is_nil(obj));
+ } else if (is_binary(obj)) {
+ LIST_TO_BITSTR_BUF_BCOPY(objp);
+ } else if (is_list(obj)) {
+ ESTACK_PUSH(s, CDR(objp));
+ continue; /* Head loop */
+ } else {
+ ASSERT(is_nil(obj));
+ }
+ break;
+ }
+
+ L_tail:
+
+ obj = CDR(objp);
+ if (is_list(obj)) {
+ continue; /* Tail loop */
+ } else if (is_binary(obj)) {
+ LIST_TO_BITSTR_BUF_BCOPY(NULL);
+ } else {
+ ASSERT(is_nil(obj));
+ }
+ break;
}
} else if (is_binary(obj)) {
- byte* bptr;
- size_t size = binary_size(obj);
- Uint bitsize;
- Uint bitoffs;
- Uint num_bits;
-
- ASSERT(size <= len);
- ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize);
- num_bits = 8*size+bitsize;
- copy_binary_to_buffer(buf, offset, bptr, bitoffs, num_bits);
- offset += bitsize;
- buf += size + (offset>7);
-#ifdef DEBUG
- len -= size + (offset>7);
-#endif
- offset = offset & 7;
+ LIST_TO_BITSTR_BUF_BCOPY(NULL);
} else {
+ if (yield_support && --yield_count <= 0)
+ goto L_yield;
ASSERT(is_nil(obj));
}
}
DESTROY_ESTACK(s);
- return offset;
+
+ if (yield_support) {
+ int reds;
+ CLEAR_SAVED_ESTACK(&state->iolist.estack);
+ reds = ((init_yield_count - yield_count - 1)
+ / ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED) + 1;
+ BUMP_REDS(state->iolist.c_p, reds);
+ state->iolist.reds_left -= reds;
+ if (state->iolist.reds_left < 0)
+ state->iolist.reds_left = 0;
+ }
+ state->buf = buf;
+ state->offset = offset;
+ return 0;
+
+L_bcopy_yield:
+
+ state->buf = buf;
+ state->offset = offset;
+#ifdef DEBUG
+ state->len = len;
+#endif
+
+ if (list_to_bitstr_buf_bcopy(state, obj, &yield_count) == 0)
+ ERTS_INTERNAL_ERROR("Missing yield");
+
+ BUMP_ALL_REDS(state->iolist.c_p);
+ state->iolist.reds_left = 0;
+ ESTACK_SAVE(s, &state->iolist.estack);
+ return ERTS_IOLIST_TO_BUF_YIELD;
+
+L_yield:
+
+ BUMP_ALL_REDS(state->iolist.c_p);
+ state->iolist.reds_left = 0;
+ state->iolist.obj = obj;
+ state->buf = buf;
+ state->offset = offset;
+ ESTACK_SAVE(s, &state->iolist.estack);
+#ifdef DEBUG
+ state->len = len;
+#endif
+ return ERTS_IOLIST_TO_BUF_YIELD;
+
+
+#undef LIST_TO_BITSTR_BUF_BCOPY_DBG
+#undef LIST_TO_BITSTR_BUF_BCOPY
+
+}
+
+static ErlDrvSizeT
+list_to_bitstr_buf_yielding(ErtsIOList2BufState *state)
+{
+ return list_to_bitstr_buf(1, state);
+}
+
+static ErlDrvSizeT
+list_to_bitstr_buf_not_yielding(ErtsIOList2BufState *state)
+{
+ return list_to_bitstr_buf(0, state);
}
static int
-bitstr_list_len(Eterm obj, Uint* num_bytes)
+list_to_bitstr_buf_bcopy(ErtsIOList2BufState *state, Eterm obj, int *yield_countp)
+{
+ int res;
+ char *buf = state->buf;
+ char *next_buf;
+ int offset = state->offset;
+ int next_offset;
+#ifdef DEBUG
+ ErlDrvSizeT len = state->len;
+ ErlDrvSizeT next_len;
+#endif
+ byte* bptr;
+ size_t size;
+ size_t max_size;
+ Uint bitoffs;
+ Uint num_bits;
+ Uint bitsize;
+ int yield_count = *yield_countp;
+
+ if (state->bcopy.bptr) {
+ bptr = state->bcopy.bptr;
+ size = state->bcopy.size;
+ bitoffs = state->bcopy.bitoffs;
+ bitsize = state->bcopy.bitsize;
+ state->bcopy.bptr = NULL;
+ }
+ else {
+
+ ASSERT(is_binary(obj));
+
+ size = binary_size(obj);
+
+ ASSERT(size <= len);
+
+ ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize);
+ }
+
+ max_size = (size_t) ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT;
+ if (yield_count > 0)
+ max_size *= (size_t) (yield_count+1);
+
+ if (size <= max_size) {
+ if (size >= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT) {
+ int cost = (int) size;
+ cost /= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT;
+ yield_count -= cost;
+ }
+ next_offset = offset + bitsize;
+ next_buf = buf + size+(next_offset>7);
+#ifdef DEBUG
+ next_len = len - size+(next_offset>7);
+#endif
+ next_offset &= 7;
+ num_bits = 8*size+bitsize;
+ res = 0;
+ }
+ else {
+ ASSERT(0 < max_size && max_size < size);
+ yield_count = 0;
+ state->bcopy.bptr = bptr + max_size;
+ state->bcopy.bitoffs = bitoffs;
+ state->bcopy.bitsize = bitsize;
+ state->bcopy.size = size - max_size;
+ next_buf = buf + max_size;
+#ifdef DEBUG
+ next_len = len - max_size;
+#endif
+ next_offset = offset;
+ num_bits = 8*max_size;
+ size = max_size;
+ res = 1;
+ }
+
+ copy_binary_to_buffer(buf, offset, bptr, bitoffs, num_bits);
+
+ state->offset = next_offset;
+ state->buf = next_buf;
+#ifdef DEBUG
+ state->len = next_len;
+#endif
+ *yield_countp = yield_count;
+
+ return res;
+}
+
+static int
+bitstr_list_len(ErtsIOListState *state)
{
Eterm* objp;
- Uint len = 0;
- Uint offs = 0;
+ Eterm obj;
+ Uint len, offs;
+ int res, init_yield_count, yield_count;
DECLARE_ESTACK(s);
+
+ if (state->reds_left <= 0)
+ return ERTS_IOLIST_YIELD;
+
+ len = (Uint) state->size;
+ offs = state->offs;
+ obj = state->obj;
+
+ ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
+ init_yield_count = ERTS_IOLIST_SIZE_YIELDS_COUNT_PER_RED;
+ init_yield_count *= state->reds_left;
+ yield_count = init_yield_count;
+ if (state->estack.start) {
+ /* Restart; restore estack... */
+ ESTACK_RESTORE(s, &state->estack);
+ }
+
goto L_again;
#define SAFE_ADD(Var, Val) \
@@ -748,46 +1431,55 @@ bitstr_list_len(Eterm obj, Uint* num_bytes)
obj = ESTACK_POP(s);
L_again:
if (is_list(obj)) {
- L_iter_list:
- objp = list_val(obj);
- /* Head */
- obj = CAR(objp);
- if (is_byte(obj)) {
- len++;
- if (len == 0) {
- goto L_overflow_error;
+ while (1) { /* Tail loop */
+ while (1) { /* Head loop */
+ if (--yield_count <= 0)
+ goto L_yield;
+ objp = list_val(obj);
+ /* Head */
+ obj = CAR(objp);
+ if (is_byte(obj)) {
+ len++;
+ if (len == 0) {
+ goto L_overflow_error;
+ }
+ } else if (is_binary(obj)) {
+ SAFE_ADD(len, binary_size(obj));
+ SAFE_ADD_BITSIZE(offs, obj);
+ } else if (is_list(obj)) {
+ ESTACK_PUSH(s, CDR(objp));
+ continue; /* Head loop */
+ } else if (is_not_nil(obj)) {
+ goto L_type_error;
+ }
+ break;
}
- } else if (is_binary(obj)) {
- SAFE_ADD(len, binary_size(obj));
- SAFE_ADD_BITSIZE(offs, obj);
- } else if (is_list(obj)) {
- ESTACK_PUSH(s, CDR(objp));
- goto L_iter_list; /* on head */
- } else if (is_not_nil(obj)) {
- goto L_type_error;
+ /* Tail */
+ obj = CDR(objp);
+ if (is_list(obj))
+ continue; /* Tail loop */
+ else if (is_binary(obj)) {
+ SAFE_ADD(len, binary_size(obj));
+ SAFE_ADD_BITSIZE(offs, obj);
+ } else if (is_not_nil(obj)) {
+ goto L_type_error;
+ }
+ break;
}
- /* Tail */
- obj = CDR(objp);
- if (is_list(obj))
- goto L_iter_list; /* on tail */
- else if (is_binary(obj)) {
+ } else {
+ if (--yield_count <= 0)
+ goto L_yield;
+ if (is_binary(obj)) {
SAFE_ADD(len, binary_size(obj));
SAFE_ADD_BITSIZE(offs, obj);
} else if (is_not_nil(obj)) {
goto L_type_error;
}
- } else if (is_binary(obj)) {
- SAFE_ADD(len, binary_size(obj));
- SAFE_ADD_BITSIZE(offs, obj);
- } else if (is_not_nil(obj)) {
- goto L_type_error;
}
}
#undef SAFE_ADD
#undef SAFE_ADD_BITSIZE
- DESTROY_ESTACK(s);
-
/*
* Make sure that the number of bits in the bitstring will fit
* in an Uint to ensure that the binary can be matched using
@@ -800,15 +1492,42 @@ bitstr_list_len(Eterm obj, Uint* num_bytes)
if (len << 3 < len) {
goto L_overflow_error;
}
- *num_bytes = len;
- return ERTS_IOLIST_OK;
+ state->size = len;
- L_type_error:
- DESTROY_ESTACK(s);
- return ERTS_IOLIST_TYPE;
+ res = ERTS_IOLIST_OK;
+
+ L_return: {
+ int yc = init_yield_count - yield_count;
+ int reds;
+
+ DESTROY_ESTACK(s);
+ CLEAR_SAVED_ESTACK(&state->estack);
+
+ reds = (yc - 1)/ERTS_IOLIST_SIZE_YIELDS_COUNT_PER_RED + 1;
+ BUMP_REDS(state->c_p, reds);
+ state->reds_left -= reds;
+ state->size = (ErlDrvSizeT) len;
+ state->have_size = 1;
+ return res;
+ }
L_overflow_error:
- DESTROY_ESTACK(s);
- return ERTS_IOLIST_OVERFLOW;
+ res = ERTS_IOLIST_OVERFLOW;
+ len = 0;
+ goto L_return;
+
+ L_type_error:
+ res = ERTS_IOLIST_TYPE;
+ len = 0;
+ goto L_return;
+
+ L_yield:
+ BUMP_ALL_REDS(state->c_p);
+ state->reds_left = 0;
+ state->size = len;
+ state->offs = offs;
+ state->obj = obj;
+ ESTACK_SAVE(s, &state->estack);
+ return ERTS_IOLIST_YIELD;
}
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index 3a987e213b..50548850eb 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -48,7 +48,8 @@ copy_object(Eterm obj, Process* to)
if (DTRACE_ENABLED(copy_object)) {
DTRACE_CHARBUF(proc_name, 64);
- erts_snprintf(proc_name, sizeof(proc_name), "%T", to->common.id);
+ erts_snprintf(proc_name, sizeof(DTRACE_CHARBUF_NAME(proc_name)),
+ "%T", to->common.id);
DTRACE2(copy_object, proc_name, size);
}
#endif
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 6ecf3f0722..ec07ddcd9c 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -851,9 +851,12 @@ erts_dsig_send_msg(ErtsDSigData *dsdp, Eterm remote, Eterm message)
#ifdef USE_VM_PROBES
*node_name = *sender_name = *receiver_name = '\0';
if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) {
- erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname);
- erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->common.id);
- erts_snprintf(receiver_name, sizeof(receiver_name), "%T", remote);
+ erts_snprintf(node_name, sizeof(DTRACE_CHARBUF_NAME(node_name)),
+ "%T", dsdp->dep->sysname);
+ erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)),
+ "%T", sender->common.id);
+ erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)),
+ "%T", remote);
msize = size_object(message);
if (token != NIL && token != am_have_dt_utag) {
tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
@@ -908,9 +911,11 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message)
#ifdef USE_VM_PROBES
*node_name = *sender_name = *receiver_name = '\0';
if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) {
- erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname);
- erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->common.id);
- erts_snprintf(receiver_name, sizeof(receiver_name),
+ erts_snprintf(node_name, sizeof(DTRACE_CHARBUF_NAME(node_name)),
+ "%T", dsdp->dep->sysname);
+ erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)),
+ "%T", sender->common.id);
+ erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)),
"{%T,%s}", remote_name, node_name);
msize = size_object(message);
if (token != NIL && token != am_have_dt_utag) {
@@ -971,11 +976,14 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote,
#ifdef USE_VM_PROBES
*node_name = *sender_name = *remote_name = '\0';
if (DTRACE_ENABLED(process_exit_signal_remote)) {
- erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname);
- erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->common.id);
- erts_snprintf(remote_name, sizeof(remote_name),
+ erts_snprintf(node_name, sizeof(DTRACE_CHARBUF_NAME(node_name)),
+ "%T", dsdp->dep->sysname);
+ erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)),
+ "%T", sender->common.id);
+ erts_snprintf(remote_name, sizeof(DTRACE_CHARBUF_NAME(remote_name)),
"{%T,%s}", remote, node_name);
- erts_snprintf(reason_str, sizeof(reason), "%T", reason);
+ erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)),
+ "%T", reason);
if (token != NIL && token != am_have_dt_utag) {
tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
@@ -1797,8 +1805,9 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy)
DTRACE_CHARBUF(port_str, 64);
DTRACE_CHARBUF(remote_str, 64);
- erts_snprintf(port_str, sizeof(port_str), "%T", cid);
- erts_snprintf(remote_str, sizeof(remote_str),
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
+ "%T", cid);
+ erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)),
"%T", dep->sysname);
DTRACE3(dist_port_not_busy, erts_this_node_sysname,
port_str, remote_str);
@@ -1855,9 +1864,11 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy)
DTRACE_CHARBUF(remote_str, 64);
DTRACE_CHARBUF(pid_str, 16);
- erts_snprintf(port_str, sizeof(port_str), "%T", cid);
- erts_snprintf(remote_str, sizeof(remote_str), "%T", dep->sysname);
- erts_snprintf(pid_str, sizeof(pid_str), "%T", c_p->common.id);
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", cid);
+ erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)),
+ "%T", dep->sysname);
+ erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)),
+ "%T", c_p->common.id);
DTRACE4(dist_port_busy, erts_this_node_sysname,
port_str, remote_str, pid_str);
}
@@ -1890,8 +1901,9 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf)
DTRACE_CHARBUF(port_str, 64);
DTRACE_CHARBUF(remote_str, 64);
- erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id);
- erts_snprintf(remote_str, sizeof(remote_str),
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
+ "%T", prt->common.id);
+ erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)),
"%T", prt->dist_entry->sysname);
DTRACE4(dist_output, erts_this_node_sysname, port_str,
remote_str, size);
@@ -1944,8 +1956,9 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
DTRACE_CHARBUF(port_str, 64);
DTRACE_CHARBUF(remote_str, 64);
- erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id);
- erts_snprintf(remote_str, sizeof(remote_str),
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
+ "%T", prt->common.id);
+ erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)),
"%T", prt->dist_entry->sysname);
DTRACE4(dist_outputv, erts_this_node_sysname, port_str,
remote_str, size);
@@ -2280,8 +2293,9 @@ erts_dist_port_not_busy(Port *prt)
DTRACE_CHARBUF(port_str, 64);
DTRACE_CHARBUF(remote_str, 64);
- erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id);
- erts_snprintf(remote_str, sizeof(remote_str),
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
+ "%T", prt->common.id);
+ erts_snprintf(remote_str, sizeof(DTRACE_CHARBUF_NAME(remote_str)),
"%T", prt->dist_entry->sysname);
DTRACE3(dist_port_not_busy, erts_this_node_sysname,
port_str, remote_str);
@@ -3246,10 +3260,10 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas
DTRACE_CHARBUF(type_str, 12);
DTRACE_CHARBUF(reason_str, 64);
- erts_snprintf(what_str, sizeof(what_str), "%T", what);
- erts_snprintf(node_str, sizeof(node_str), "%T", node);
- erts_snprintf(type_str, sizeof(type_str), "%T", type);
- erts_snprintf(reason_str, sizeof(reason_str), "%T", reason);
+ erts_snprintf(what_str, sizeof(DTRACE_CHARBUF_NAME(what_str)), "%T", what);
+ erts_snprintf(node_str, sizeof(DTRACE_CHARBUF_NAME(node_str)), "%T", node);
+ erts_snprintf(type_str, sizeof(DTRACE_CHARBUF_NAME(type_str)), "%T", type);
+ erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)), "%T", reason);
DTRACE5(dist_monitor, erts_this_node_sysname,
what_str, node_str, type_str, reason_str);
}
diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h
index 0519a9225e..f32b999198 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -40,6 +40,7 @@
#define DFLAG_SMALL_ATOM_TAGS 0x4000
#define DFLAG_INTERNAL_TAGS 0x8000
#define DFLAG_UTF8_ATOMS 0x10000
+#define DFLAG_MAP_TAG 0x20000
/* All flags that should be enabled when term_to_binary/1 is used. */
#define TERM_TO_BINARY_DFLAGS (DFLAG_EXTENDED_REFERENCES \
@@ -47,7 +48,8 @@
| DFLAG_NEW_FLOATS \
| DFLAG_EXTENDED_PIDS_PORTS \
| DFLAG_EXPORT_PTR_TAG \
- | DFLAG_BIT_BINARIES)
+ | DFLAG_BIT_BINARIES \
+ | DFLAG_MAP_TAG)
/* opcodes used in distribution messages */
#define DOP_LINK 1
diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h
index 942eaa47d0..d3109b9432 100644
--- a/erts/emulator/beam/erl_alloc.h
+++ b/erts/emulator/beam/erl_alloc.h
@@ -492,7 +492,7 @@ static TYPE * \
NAME##_alloc(void) \
{ \
ErtsSchedulerData *esdp = erts_get_scheduler_data(); \
- if (!esdp) \
+ if (!esdp || ERTS_SCHEDULER_IS_DIRTY(esdp)) \
return NULL; \
return (TYPE *) erts_sspa_alloc(sspa_data_##NAME##__, \
(int) esdp->no - 1); \
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index b3dc327704..decae6b2ca 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -292,7 +292,8 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q)
if (DTRACE_ENABLED(aio_pool_add)) {
DTRACE_CHARBUF(port_str, 16);
- erts_snprintf(port_str, sizeof(port_str), "%T", a->port);
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
+ "%T", a->port);
/* DTRACE TODO: Get the queue length from erts_thr_q_enqueue() ? */
len = -1;
DTRACE2(aio_pool_add, port_str, len);
@@ -327,7 +328,8 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
if (DTRACE_ENABLED(aio_pool_get)) {
DTRACE_CHARBUF(port_str, 16);
- erts_snprintf(port_str, sizeof(port_str), "%T", a->port);
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
+ "%T", a->port);
/* DTRACE TODO: Get the length from erts_thr_q_dequeue() ? */
len = -1;
DTRACE2(aio_pool_get, port_str, len);
diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c
index ff775691b3..7e0e825a0d 100644
--- a/erts/emulator/beam/erl_bif_binary.c
+++ b/erts/emulator/beam/erl_bif_binary.c
@@ -2294,18 +2294,11 @@ BIF_RETTYPE binary_bin_to_list_1(BIF_ALIST_1)
BIF_ERROR(BIF_P,BADARG);
}
-/*
- * Ok, erlang:list_to_binary does not interrupt, and we really don't want
- * an alternative implementation for the exact same thing, why we
- * have descided to use the old non-restarting implementation for now.
- * In reality, there are seldom many iterations involved in doing this, so the
- * problem of long-running bifs is not really that big in this case.
- * So, for now we use the old implementation also in the module binary.
- */
+HIPE_WRAPPER_BIF_DISABLE_GC(binary_list_to_bin, 1)
BIF_RETTYPE binary_list_to_bin_1(BIF_ALIST_1)
{
- return erts_list_to_binary_bif(BIF_P, BIF_ARG_1);
+ return erts_list_to_binary_bif(BIF_P, BIF_ARG_1, bif_export[BIF_binary_list_to_bin_1]);
}
typedef struct {
diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c
index 1728b200f7..56cd2ba04f 100644
--- a/erts/emulator/beam/erl_bif_ddll.c
+++ b/erts/emulator/beam/erl_bif_ddll.c
@@ -1548,8 +1548,10 @@ static int do_load_driver_entry(DE_Handle *dh, char *path, char *name)
switch (dp->extended_marker) {
case ERL_DRV_EXTENDED_MARKER:
- if (ERL_DRV_EXTENDED_MAJOR_VERSION != dp->major_version
- || ERL_DRV_EXTENDED_MINOR_VERSION < dp->minor_version) {
+ if (dp->major_version < ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD
+ || (ERL_DRV_EXTENDED_MAJOR_VERSION < dp->major_version
+ || (ERL_DRV_EXTENDED_MAJOR_VERSION == dp->major_version
+ && ERL_DRV_EXTENDED_MINOR_VERSION < dp->minor_version))) {
/* Incompatible driver version */
res = ERL_DE_LOAD_ERROR_INCORRECT_VERSION;
goto error;
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 2adba9b240..4d5e55aaf5 100755
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -2691,6 +2691,11 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
else if (ERTS_IS_ATOM_STR("ets_limit",BIF_ARG_1)) {
BIF_RET(make_small(erts_db_get_max_tabs()));
}
+ else if (ERTS_IS_ATOM_STR("tolerant_timeofday",BIF_ARG_1)) {
+ BIF_RET(erts_disable_tolerant_timeofday
+ ? am_disabled
+ : am_enabled);
+ }
BIF_ERROR(BIF_P, BADARG);
}
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 77627a6897..afb33c1cdb 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -882,7 +882,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
dtrace_proc_str(p, process_str);
- erts_snprintf(port_str, sizeof(port_str), "%T", port->common.id);
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", port->common.id);
DTRACE3(port_open, process_str, name_buf, port_str);
}
#endif
diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h
index 819b19e566..6c9f53ce87 100644
--- a/erts/emulator/beam/erl_binary.h
+++ b/erts/emulator/beam/erl_binary.h
@@ -166,7 +166,7 @@ Eterm erts_bin_bytes_to_list(Eterm previous, Eterm* hp, byte* bytes, Uint size,
* Common implementation for erlang:list_to_binary/1 and binary:list_to_bin/1
*/
-BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg);
+BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg, Export *bif);
BIF_RETTYPE erts_gc_binary_part(Process *p, Eterm *reg, Eterm live, int range_is_tuple);
BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen);
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index a5d67571e2..8f246ffa07 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -259,10 +259,11 @@ static void schedule_free_dbtable(DbTable* tb)
/*
* NON-SMP case: Caller is *not* allowed to access the *tb
* structure after this function has returned!
- * SMP case: Caller is allowed to access the *tb structure
- * until the bif has returned (we typically
- * need to unlock the table lock after this
- * function has returned).
+ * SMP case: Caller is allowed to access the *common* part of the *tb
+ * structure until the bif has returned (we typically need to
+ * unlock the table lock after this function has returned).
+ * 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);
erts_schedule_thr_prgr_later_cleanup_op(free_dbtable,
@@ -3279,34 +3280,37 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks)
}
erts_smp_rwmtx_runlock(mmtl);
if (tb) {
- int reds;
- DbFixation** pp;
+ int reds = 0;
db_lock(tb, LCK_WRITE_REC);
- #ifdef ERTS_SMP
- erts_smp_mtx_lock(&tb->common.fixlock);
- #endif
- reds = 10;
-
- for (pp = &tb->common.fixations; *pp != NULL;
- pp = &(*pp)->next) {
- if ((*pp)->pid == pid) {
- DbFixation* fix = *pp;
- erts_aint_t diff = -((erts_aint_t) fix->counter);
- erts_refc_add(&tb->common.ref,diff,0);
- *pp = fix->next;
- erts_db_free(ERTS_ALC_T_DB_FIXATION,
- tb, fix, sizeof(DbFixation));
- ERTS_ETS_MISC_MEM_ADD(-sizeof(DbFixation));
- break;
+ if (!(tb->common.status & DB_DELETE)) {
+ DbFixation** pp;
+
+ #ifdef ERTS_SMP
+ erts_smp_mtx_lock(&tb->common.fixlock);
+ #endif
+ reds = 10;
+
+ for (pp = &tb->common.fixations; *pp != NULL;
+ pp = &(*pp)->next) {
+ if ((*pp)->pid == pid) {
+ DbFixation* fix = *pp;
+ erts_aint_t diff = -((erts_aint_t) fix->counter);
+ erts_refc_add(&tb->common.ref,diff,0);
+ *pp = fix->next;
+ erts_db_free(ERTS_ALC_T_DB_FIXATION,
+ tb, fix, sizeof(DbFixation));
+ ERTS_ETS_MISC_MEM_ADD(-sizeof(DbFixation));
+ break;
+ }
+ }
+ #ifdef ERTS_SMP
+ erts_smp_mtx_unlock(&tb->common.fixlock);
+ #endif
+ if (!IS_FIXED(tb) && IS_HASH_TABLE(tb->common.status)) {
+ db_unfix_table_hash(&(tb->hash));
+ reds += 40;
}
- }
- #ifdef ERTS_SMP
- erts_smp_mtx_unlock(&tb->common.fixlock);
- #endif
- if (!IS_FIXED(tb) && IS_HASH_TABLE(tb->common.status)) {
- db_unfix_table_hash(&(tb->hash));
- reds += 40;
}
db_unlock(tb, LCK_WRITE_REC);
BUMP_REDS(c_p, reds);
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index 25029ba90f..a62a83a928 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -485,7 +485,7 @@ static int db_first_tree(Process *p, DbTable *tbl, Eterm *ret)
*ret = am_EOT;
return DB_ERROR_NONE;
}
- /* Walk down to the tree to the left */
+ /* Walk down the tree to the left */
if ((stack = get_static_stack(tb)) != NULL) {
stack->pos = stack->slot = 0;
}
@@ -531,7 +531,7 @@ static int db_last_tree(Process *p, DbTable *tbl, Eterm *ret)
*ret = am_EOT;
return DB_ERROR_NONE;
}
- /* Walk down to the tree to the left */
+ /* Walk down the tree to the right */
if ((stack = get_static_stack(tb)) != NULL) {
stack->pos = stack->slot = 0;
}
diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h
index 5517c26ba4..5ced8c5ca0 100644
--- a/erts/emulator/beam/erl_driver.h
+++ b/erts/emulator/beam/erl_driver.h
@@ -136,6 +136,22 @@ typedef struct {
#define ERL_DRV_EXTENDED_MINOR_VERSION 0
/*
+ * The emulator will refuse to load a driver with a major version
+ * lower than ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD. The load
+ * may however fail if user have not removed use of deprecated
+ * symbols.
+ *
+ * The ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD have to allow
+ * loading of drivers built at least two major OTP releases
+ * ago.
+ *
+ * Bump of major version to 3 happened in OTP 17. That is, in
+ * OTP 19 we can increase ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD
+ * to 3.
+ */
+#define ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD 2
+
+/*
* The emulator will refuse to load a driver with different major
* version than the one used by the emulator.
*/
@@ -182,7 +198,7 @@ typedef long long ErlDrvSInt64;
#error No 64-bit integer type
#endif
-#if defined(__WIN32__)
+#if defined(__WIN32__) || defined(_WIN32)
typedef ErlDrvUInt ErlDrvSizeT;
typedef ErlDrvSInt ErlDrvSSizeT;
#else
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index d54658f1ea..5e6d812242 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -324,7 +324,6 @@ erl_init(int ncpu,
BIN_VH_MIN_SIZE = erts_next_heap_size(BIN_VH_MIN_SIZE, 0);
erts_init_trace();
- erts_init_binary();
erts_init_bits();
erts_code_ix_init();
erts_init_fun_table();
@@ -337,6 +336,7 @@ erl_init(int ncpu,
erts_ddll_init();
init_emulator();
erts_ptab_init(); /* Must be after init_emulator() */
+ erts_init_binary(); /* Must be after init_emulator() */
erts_bp_init();
init_db(); /* Must be after init_emulator */
erts_bif_timer_init();
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 7e3a90779d..c13eb87012 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -270,9 +270,9 @@ union erts_lc_free_block_t_ {
static ethr_tsd_key locks_key;
-static erts_lc_locked_locks_t *erts_locked_locks;
+static erts_lc_locked_locks_t *erts_locked_locks = NULL;
-static erts_lc_free_block_t *free_blocks;
+static erts_lc_free_block_t *free_blocks = NULL;
#ifdef ERTS_LC_STATIC_ALLOC
#define ERTS_LC_FB_CHUNK_SIZE 10000
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 2fff7f9390..5e740aacdd 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -58,6 +58,8 @@
* - maps:size/1
* - maps:without/2
*
+ * DEBUG: for sharing calculation
+ * - erts_internal:map_to_tuple_keys/1
*/
/* erlang:map_size/1
@@ -647,22 +649,24 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
*mhp++ = tup;
if (is_immed(key)) {
- while(n--) {
+ while (1) {
if (*ks == key) {
goto found_key;
- } else {
+ } else if (--n) {
*mhp++ = *vs++;
*thp++ = *ks++;
- }
+ } else
+ break;
}
} else {
- while(n--) {
+ while(1) {
if (EQ(*ks, key)) {
goto found_key;
- } else {
+ } else if (--n) {
*mhp++ = *vs++;
*thp++ = *ks++;
- }
+ } else
+ break;
}
}
@@ -676,7 +680,7 @@ int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) {
found_key:
/* Copy rest of keys and values */
- if (n) {
+ if (--n) {
sys_memcpy(mhp, vs+1, n*sizeof(Eterm));
sys_memcpy(thp, ks+1, n*sizeof(Eterm));
}
@@ -817,3 +821,17 @@ int erts_validate_and_sort_map(map_t* mp)
}
return 1;
}
+
+/*
+ * erts_internal:map_to_tuple_keys/1
+ *
+ * Used in erts_debug:size/1
+ */
+
+BIF_RETTYPE erts_internal_map_to_tuple_keys_1(BIF_ALIST_1) {
+ if (is_map(BIF_ARG_1)) {
+ map_t *mp = (map_t*)map_val(BIF_ARG_1);
+ BIF_RET(mp->keys);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+}
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 6a9030fd99..59a677a12c 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -896,8 +896,10 @@ erts_send_message(Process* sender,
#ifdef USE_VM_PROBES
*sender_name = *receiver_name = '\0';
if (DTRACE_ENABLED(message_send)) {
- erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), "%T", sender->common.id);
- erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), "%T", receiver->common.id);
+ erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)),
+ "%T", sender->common.id);
+ erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)),
+ "%T", receiver->common.id);
}
#endif
if (SEQ_TRACE_TOKEN(sender) != NIL && !(flags & ERTS_SND_FLG_NO_SEQ_TRACE)) {
@@ -1030,7 +1032,6 @@ erts_send_message(Process* sender,
}
BM_SWAP_TIMER(send,system);
} else {
-#ifdef ERTS_SMP
ErlOffHeap *ohp;
Eterm *hp;
erts_aint32_t state;
@@ -1062,42 +1063,6 @@ erts_send_message(Process* sender,
#endif
);
BM_SWAP_TIMER(send,system);
-#else
- ErlMessage* mp = message_alloc();
- Eterm *hp;
- BM_SWAP_TIMER(send,size);
- msize = size_object(message);
- BM_SWAP_TIMER(size,send);
-
- if (receiver->stop - receiver->htop <= msize) {
- BM_SWAP_TIMER(send,system);
- erts_garbage_collect(receiver, msize, receiver->arg_reg, receiver->arity);
- BM_SWAP_TIMER(system,send);
- }
- hp = receiver->htop;
- receiver->htop = hp + msize;
- BM_SWAP_TIMER(send,copy);
- message = copy_struct(message, msize, &hp, &receiver->off_heap);
- BM_MESSAGE_COPIED(msize);
- BM_SWAP_TIMER(copy,send);
- DTRACE6(message_send, sender_name, receiver_name,
- (uint32_t)msize, tok_label, tok_lastcnt, tok_serial);
- ERL_MESSAGE_TERM(mp) = message;
- ERL_MESSAGE_TOKEN(mp) = NIL;
-#ifdef USE_VM_PROBES
- ERL_MESSAGE_DT_UTAG(mp) = NIL;
-#endif
- mp->next = NULL;
- mp->data.attached = NULL;
- LINK_MESSAGE(receiver, mp);
- res = receiver->msg.len;
- erts_proc_notify_new_message(receiver);
-
- if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) {
- trace_receive(receiver, message);
- }
- BM_SWAP_TIMER(send,system);
-#endif /* #ifndef ERTS_SMP */
}
return res;
}
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 40860e141c..ff551ea3af 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1515,26 +1515,35 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent)
#ifdef ERTS_DIRTY_SCHEDULERS
+/* NIFs exports need one more item than the Export struct provides, the
+ * erl_module_nif*, so the DirtyNifExport below adds that. The Export
+ * member must be first in the struct.
+ */
+typedef struct {
+ Export exp;
+ struct erl_module_nif* m;
+} DirtyNifExport;
+
static void
-alloc_proc_psd(Process* proc, Export **ep)
+alloc_proc_psd(Process* proc, DirtyNifExport **ep)
{
int i;
if (!*ep) {
- *ep = erts_alloc(ERTS_ALC_T_PSD, sizeof(Export));
- sys_memset((void*) *ep, 0, sizeof(Export));
+ *ep = erts_alloc(ERTS_ALC_T_PSD, sizeof(DirtyNifExport));
+ sys_memset((void*) *ep, 0, sizeof(DirtyNifExport));
for (i=0; i<ERTS_NUM_CODE_IX; i++) {
- (*ep)->addressv[i] = &(*ep)->code[3];
+ (*ep)->exp.addressv[i] = &(*ep)->exp.code[3];
}
- (*ep)->code[3] = (BeamInstr) em_call_nif;
+ (*ep)->exp.code[3] = (BeamInstr) em_call_nif;
}
- (void) ERTS_PROC_SET_DIRTY_SCHED_TRAP_EXPORT(proc, ERTS_PROC_LOCK_MAIN, *ep);
+ (void) ERTS_PROC_SET_DIRTY_SCHED_TRAP_EXPORT(proc, ERTS_PROC_LOCK_MAIN, &(*ep)->exp);
}
static ERL_NIF_TERM
execute_dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
Eterm* reg = ERTS_PROC_GET_SCHDATA(env->proc)->x_reg_array;
- ERL_NIF_TERM result = (ERL_NIF_TERM) reg[0];
+ ERL_NIF_TERM result, dirty_result = (ERL_NIF_TERM) reg[0];
typedef ERL_NIF_TERM (*FinalizerFP)(ErlNifEnv*, ERL_NIF_TERM);
FinalizerFP fp;
#if HAVE_INT64 && SIZEOF_LONG != 8
@@ -1544,7 +1553,11 @@ execute_dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ASSERT(sizeof(fp) <= sizeof(unsigned long));
enif_get_ulong(env, reg[1], (unsigned long *) &fp);
#endif
- return (*fp)(env, result);
+ result = (*fp)(env, dirty_result);
+ if (erts_refc_dectest(&env->mod_nif->rt_dtor_cnt, 0) == 0
+ && env->mod_nif->mod == NULL)
+ close_lib(env->mod_nif);
+ return result;
}
#endif /* ERTS_DIRTY_SCHEDULERS */
@@ -1560,7 +1573,7 @@ enif_schedule_dirty_nif(ErlNifEnv* env, int flags,
erts_aint32_t state, n, a;
Process* proc = env->proc;
Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array;
- Export* ep = NULL;
+ DirtyNifExport* ep = NULL;
int i;
int chkflgs = (flags & (ERL_NIF_DIRTY_JOB_IO_BOUND|ERL_NIF_DIRTY_JOB_CPU_BOUND));
@@ -1585,17 +1598,20 @@ enif_schedule_dirty_nif(ErlNifEnv* env, int flags,
if (a == state)
break;
}
- if (!(ep = ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc)))
+ if (!(ep = (DirtyNifExport*) ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc)))
alloc_proc_psd(proc, &ep);
ERTS_VBUMP_ALL_REDS(proc);
- ep->code[2] = argc;
+ ep->exp.code[2] = argc;
for (i = 0; i < argc; i++) {
reg[i] = (Eterm) argv[i];
}
- proc->i = (BeamInstr*) ep->addressv[0];
- ep->code[4] = (BeamInstr) fp;
+ proc->i = (BeamInstr*) ep->exp.addressv[0];
+ ep->exp.code[4] = (BeamInstr) fp;
+ ep->m = env->mod_nif;
proc->freason = TRAP;
+ erts_refc_inc(&env->mod_nif->rt_dtor_cnt, 1);
+
return THE_NON_VALUE;
#else
return (*fp)(env, argc, argv);
@@ -1609,17 +1625,17 @@ enif_schedule_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result,
#ifdef USE_THREADS
Process* proc = env->proc;
Eterm* reg = ERTS_PROC_GET_SCHDATA(proc)->x_reg_array;
- Export* ep;
+ DirtyNifExport* ep;
erts_smp_atomic32_read_band_mb(&proc->state,
~(ERTS_PSFLG_DIRTY_CPU_PROC
|ERTS_PSFLG_DIRTY_IO_PROC
|ERTS_PSFLG_DIRTY_CPU_PROC_IN_Q
|ERTS_PSFLG_DIRTY_IO_PROC_IN_Q));
- if (!(ep = ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc)))
+ if (!(ep = (DirtyNifExport*) ERTS_PROC_GET_DIRTY_SCHED_TRAP_EXPORT(proc)))
alloc_proc_psd(proc, &ep);
ERTS_VBUMP_ALL_REDS(proc);
- ep->code[2] = 2;
+ ep->exp.code[2] = 2;
reg[0] = (Eterm) result;
#if HAVE_INT64 && SIZEOF_LONG != 8
ASSERT(sizeof(fp) <= sizeof(ErlNifUInt64));
@@ -1628,8 +1644,8 @@ enif_schedule_dirty_nif_finalizer(ErlNifEnv* env, ERL_NIF_TERM result,
ASSERT(sizeof(fp) <= sizeof(unsigned long));
reg[1] = (Eterm) enif_make_ulong(env, (unsigned long) fp);
#endif
- proc->i = (BeamInstr*) ep->addressv[0];
- ep->code[4] = (BeamInstr) execute_dirty_nif_finalizer;
+ proc->i = (BeamInstr*) ep->exp.addressv[0];
+ ep->exp.code[4] = (BeamInstr) execute_dirty_nif_finalizer;
proc->freason = TRAP;
return THE_NON_VALUE;
@@ -2049,8 +2065,10 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
(entry = erts_sys_ddll_call_nif_init(init_func)) == NULL)) {
ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful");
}
- else if (entry->major != ERL_NIF_MAJOR_VERSION
- || entry->minor > ERL_NIF_MINOR_VERSION
+ else if (entry->major < ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD
+ || (ERL_NIF_MAJOR_VERSION < entry->major
+ || (ERL_NIF_MAJOR_VERSION == entry->major
+ && ERL_NIF_MINOR_VERSION < entry->minor))
|| (entry->major==2 && entry->minor == 5)) { /* experimental maps */
ret = load_nif_error(BIF_P, bad_lib, "Library version (%d.%d) not compatible (with %d.%d).",
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index c12ba4d554..5b93c2398e 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -46,6 +46,18 @@
#define ERL_NIF_MAJOR_VERSION 2
#define ERL_NIF_MINOR_VERSION 6
+/*
+ * The emulator will refuse to load a nif-lib with a major version
+ * lower than ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD. The load
+ * may however fail if user have not removed use of deprecated
+ * symbols.
+ *
+ * The ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD have to allow
+ * loading of nif-libs built at least two major OTP releases
+ * ago.
+ */
+#define ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD 2
+
#include <stdlib.h>
#ifdef SIZEOF_CHAR
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index fb6048b41f..31d9a1e26e 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -2035,9 +2035,9 @@ begin_port_cleanup(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p)
DTRACE_CHARBUF(pid_str, 16);
ErtsProcList* plp2 = plp;
- erts_snprintf(port_str, sizeof(port_str), "%T", pp->common.id);
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", pp->common.id);
while (plp2 != NULL) {
- erts_snprintf(pid_str, sizeof(pid_str), "%T", plp2->pid);
+ erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->pid);
DTRACE2(process_port_unblocked, pid_str, port_str);
}
}
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 37e1d07107..b73f9b7f92 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -2672,6 +2672,13 @@ aux_thread(void *unused)
ErtsThrPrgrCallbacks callbacks;
int thr_prgr_active = 1;
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ {
+ char buf[] = "aux_thread";
+ erts_lc_set_thread_name(buf);
+ }
+#endif
+
ssi->event = erts_tse_fetch();
callbacks.arg = (void *) ssi;
@@ -6144,7 +6151,7 @@ suspend_process(Process *c_p, Process *p)
if (c_p == p) {
state = erts_smp_atomic32_read_bor_relb(&p->state,
ERTS_PSFLG_SUSPENDED);
- ASSERT(state & ERTS_PSFLG_RUNNING);
+ ASSERT(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS));
suspended = (state & ERTS_PSFLG_SUSPENDED) ? -1: 1;
}
else {
@@ -9207,7 +9214,6 @@ Process *schedule(Process *p, int calls)
*/
pick_next_process: {
erts_aint32_t psflg_band_mask;
- erts_aint32_t running_flag;
int prio_q;
int qmask;
@@ -9269,12 +9275,6 @@ Process *schedule(Process *p, int calls)
state = erts_smp_atomic32_read_nob(&p->state);
}
-
- if (state & ERTS_PSFLG_ACTIVE_SYS)
- running_flag = ERTS_PSFLG_RUNNING_SYS;
- else
- running_flag = ERTS_PSFLG_RUNNING;
-
while (1) {
erts_aint32_t exp, new, tmp;
tmp = new = exp = state;
@@ -9284,8 +9284,12 @@ Process *schedule(Process *p, int calls)
tmp = state & (ERTS_PSFLG_SUSPENDED
| ERTS_PSFLG_PENDING_EXIT
| ERTS_PSFLG_ACTIVE_SYS);
- if (tmp != ERTS_PSFLG_SUSPENDED)
- new |= running_flag;
+ if (tmp != ERTS_PSFLG_SUSPENDED) {
+ if (state & ERTS_PSFLG_ACTIVE_SYS)
+ new |= ERTS_PSFLG_RUNNING_SYS;
+ else
+ new |= ERTS_PSFLG_RUNNING;
+ }
}
state = erts_smp_atomic32_cmpxchg_relb(&p->state, new, exp);
if (state == exp) {
@@ -11313,7 +11317,7 @@ send_exit_signal(Process *c_p, /* current process if and only
dtrace_pid_str(from, sender_str);
dtrace_proc_str(rp, receiver_str);
- erts_snprintf(reason_buf, sizeof(reason_buf) - 1, "%T", reason);
+ erts_snprintf(reason_buf, sizeof(DTRACE_CHARBUF_NAME(reason_buf)) - 1, "%T", reason);
DTRACE3(process_exit_signal, sender_str, receiver_str, reason_buf);
}
#endif
diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h
index f10a3a9d38..37014ccf94 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -112,11 +112,11 @@ struct erl_node_; /* Declared in erl_node_tables.h */
* 1000 REFC_BINARY | |
* 1001 HEAP_BINARY | BINARIES |
* 1010 SUB_BINARY | |
- * 1011 Not used
+ * 1011 Not used; see comment below
* 1100 EXTERNAL_PID | |
* 1101 EXTERNAL_PORT | EXTERNAL THINGS |
* 1110 EXTERNAL_REF | |
- * 1111 Not used
+ * 1111 MAP
*
* COMMENTS:
*
@@ -140,10 +140,11 @@ struct erl_node_; /* Declared in erl_node_tables.h */
#define REFC_BINARY_SUBTAG (0x8 << _TAG_PRIMARY_SIZE) /* BINARY */
#define HEAP_BINARY_SUBTAG (0x9 << _TAG_PRIMARY_SIZE) /* BINARY */
#define SUB_BINARY_SUBTAG (0xA << _TAG_PRIMARY_SIZE) /* BINARY */
-#define MAP_SUBTAG (0xB << _TAG_PRIMARY_SIZE) /* MAP */
+/* _BINARY_XXX_MASK depends on 0xB being unused */
#define EXTERNAL_PID_SUBTAG (0xC << _TAG_PRIMARY_SIZE) /* EXTERNAL_PID */
#define EXTERNAL_PORT_SUBTAG (0xD << _TAG_PRIMARY_SIZE) /* EXTERNAL_PORT */
#define EXTERNAL_REF_SUBTAG (0xE << _TAG_PRIMARY_SIZE) /* EXTERNAL_REF */
+#define MAP_SUBTAG (0xF << _TAG_PRIMARY_SIZE) /* MAP */
#define _TAG_HEADER_ARITYVAL (TAG_PRIMARY_HEADER|ARITYVAL_SUBTAG)
@@ -156,11 +157,11 @@ struct erl_node_; /* Declared in erl_node_tables.h */
#define _TAG_HEADER_REFC_BIN (TAG_PRIMARY_HEADER|REFC_BINARY_SUBTAG)
#define _TAG_HEADER_HEAP_BIN (TAG_PRIMARY_HEADER|HEAP_BINARY_SUBTAG)
#define _TAG_HEADER_SUB_BIN (TAG_PRIMARY_HEADER|SUB_BINARY_SUBTAG)
-#define _TAG_HEADER_MAP (TAG_PRIMARY_HEADER|MAP_SUBTAG)
#define _TAG_HEADER_EXTERNAL_PID (TAG_PRIMARY_HEADER|EXTERNAL_PID_SUBTAG)
#define _TAG_HEADER_EXTERNAL_PORT (TAG_PRIMARY_HEADER|EXTERNAL_PORT_SUBTAG)
#define _TAG_HEADER_EXTERNAL_REF (TAG_PRIMARY_HEADER|EXTERNAL_REF_SUBTAG)
#define _TAG_HEADER_BIN_MATCHSTATE (TAG_PRIMARY_HEADER|BIN_MATCHSTATE_SUBTAG)
+#define _TAG_HEADER_MAP (TAG_PRIMARY_HEADER|MAP_SUBTAG)
#define _TAG_HEADER_MASK 0x3F
@@ -892,7 +893,8 @@ typedef struct external_thing_ {
(((x) & _TAG_HEADER_MASK) == _TAG_HEADER_EXTERNAL_REF)
#define is_external_header(x) \
- (((x) & (_TAG_HEADER_MASK-_BINARY_XXX_MASK)) == _TAG_HEADER_EXTERNAL_PID)
+ (((x) & (_TAG_HEADER_MASK-_BINARY_XXX_MASK)) == _TAG_HEADER_EXTERNAL_PID \
+ && ((x) & _TAG_HEADER_MASK) != _TAG_HEADER_MAP)
#define is_external(x) (is_boxed((x)) && is_external_header(*boxed_val((x))))
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 6978a5f11a..ea5c850a30 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -151,6 +151,11 @@ do { \
message dispatcher thread takes care of that). */
#define ERTS_GET_TRACER_REF(RES, TPID, TRACEE_FLGS) \
do { (RES) = (TPID); } while(0)
+int
+erts_is_tracer_proc_valid(Process* p)
+{
+ return 1;
+}
#else
#define ERTS_NULL_TRACER_REF NULL
#define ERTS_TRACER_REF_TYPE Process *
@@ -163,6 +168,20 @@ do { \
return; \
} \
} while (0)
+int
+erts_is_tracer_proc_valid(Process* p)
+{
+ Process* tracer;
+
+ tracer = erts_proc_lookup(ERTS_TRACER_PROC(p));
+ if (tracer && ERTS_TRACE_FLAGS(tracer) & F_TRACER) {
+ return 1;
+ } else {
+ ERTS_TRACER_PROC(p) = NIL;
+ ERTS_TRACE_FLAGS(p) = ~TRACEE_FLAGS;
+ return 0;
+ }
+}
#endif
static Uint active_sched;
@@ -2505,7 +2524,7 @@ monitor_large_heap(Process *p) {
#ifndef ERTS_SMP
ASSERT(is_internal_pid(system_monitor));
monitor_p = erts_proc_lookup(system_monitor);
- if (monitor_p || p == monitor_p) {
+ if (!monitor_p || p == monitor_p) {
return;
}
#endif
diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h
index 853c6cb0d8..4f2c70d6e7 100644
--- a/erts/emulator/beam/erl_trace.h
+++ b/erts/emulator/beam/erl_trace.h
@@ -39,6 +39,7 @@ void erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp);
void erts_get_default_tracing(Uint *flagsp, Eterm *tracerp);
void erts_set_system_monitor(Eterm monitor);
Eterm erts_get_system_monitor(void);
+int erts_is_tracer_proc_valid(Process* p);
#ifdef ERTS_SMP
void erts_check_my_tracer_proc(Process *);
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 3a968594f3..f8e1431a53 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -2126,6 +2126,8 @@ Eterm erts_convert_native_to_filename(Process *p, byte *bytes)
mac = 1;
case ERL_FILENAME_UTF8:
size = strlen((char *) bytes);
+ if (size == 0)
+ return NIL;
if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK) {
goto noconvert;
}
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 9671cde228..8d240355b0 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -44,9 +44,6 @@
#include "erl_zlib.h"
#include "erl_map.h"
-#ifdef HIPE
-#include "hipe_mode_switch.h"
-#endif
#define in_area(ptr,start,nbytes) ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
#define MAX_STRING_LEN 0xffff
@@ -111,26 +108,17 @@ static int encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acm
static Export binary_to_term_trap_export;
static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1);
-static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b);
+static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b,
+ Export *bif, Eterm arg0, Eterm arg1);
void erts_init_external(void) {
-#if 1 /* In R16 */
erts_init_trap_export(&term_to_binary_trap_export,
- am_erlang, am_term_to_binary_trap, 1,
+ am_erts_internal, am_term_to_binary_trap, 1,
&term_to_binary_trap_1);
erts_init_trap_export(&binary_to_term_trap_export,
- am_erlang, am_binary_to_term_trap, 1,
+ am_erts_internal, am_binary_to_term_trap, 1,
&binary_to_term_trap_1);
-#else
- sys_memset((void *) &term_to_binary_trap_export, 0, sizeof(Export));
- term_to_binary_trap_export.address = &term_to_binary_trap_export.code[3];
- term_to_binary_trap_export.code[0] = am_erlang;
- term_to_binary_trap_export.code[1] = am_term_to_binary_trap;
- term_to_binary_trap_export.code[2] = 1;
- term_to_binary_trap_export.code[3] = (BeamInstr) em_apply_bif;
- term_to_binary_trap_export.code[4] = (BeamInstr) &term_to_binary_trap_1;
-#endif
return;
}
@@ -1069,6 +1057,8 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1)
}
}
+HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 1)
+
BIF_RETTYPE term_to_binary_1(BIF_ALIST_1)
{
Eterm res = erts_term_to_binary_int(BIF_P, BIF_ARG_1, 0, TERM_TO_BINARY_DFLAGS, NULL);
@@ -1081,6 +1071,8 @@ BIF_RETTYPE term_to_binary_1(BIF_ALIST_1)
}
}
+HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 2)
+
BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
{
Process* p = BIF_P;
@@ -1185,6 +1177,8 @@ typedef struct B2TContext_t {
Uint32 flags;
SWord reds;
Eterm trap_bin;
+ Export *bif;
+ Eterm arg[2];
enum B2TState state;
union {
B2TSizeContext sc;
@@ -1356,7 +1350,8 @@ static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1)
Binary *context_bin = ((ProcBin *) binary_val(BIF_ARG_1))->val;
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(context_bin) == b2t_context_destructor);
- return binary_to_term_int(BIF_P, 0, THE_NON_VALUE, context_bin);
+ return binary_to_term_int(BIF_P, 0, THE_NON_VALUE, context_bin, NULL,
+ THE_NON_VALUE, THE_NON_VALUE);
}
@@ -1391,8 +1386,10 @@ static B2TContext* b2t_export_context(Process* p, B2TContext* src)
return ctx;
}
-static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b)
+static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* context_b,
+ Export *bif_init, Eterm arg0, Eterm arg1)
{
+ BIF_RETTYPE ret_val;
#ifdef EXTREME_B2T_TRAPPING
SWord initial_reds = 1 + b2t_rand() % 4;
#else
@@ -1409,6 +1406,9 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con
ctx->state = B2TPrepare;
ctx->aligned_alloc = NULL;
ctx->flags = flags;
+ ctx->bif = bif_init;
+ ctx->arg[0] = arg0;
+ ctx->arg[1] = arg1;
IF_DEBUG(ctx->trap_bin = THE_NON_VALUE;)
} else {
is_first_call = 0;
@@ -1504,12 +1504,24 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con
HRelease(p, ctx->u.dc.hp_end, ctx->u.dc.hp_start);
/*fall through*/
case B2TBadArg:
- b2t_destroy_context(ctx);
- if (!is_first_call) {
- erts_set_gc_state(p, 1);
- }
BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION);
- BIF_ERROR(p, BADARG & ~EXF_SAVETRACE);
+
+ ASSERT(ctx->bif == bif_export[BIF_binary_to_term_1]
+ || ctx->bif == bif_export[BIF_binary_to_term_2]);
+
+ if (is_first_call)
+ ERTS_BIF_PREP_ERROR(ret_val, p, BADARG);
+ else {
+ erts_set_gc_state(p, 1);
+ if (is_non_value(ctx->arg[1]))
+ ERTS_BIF_PREP_ERROR_TRAPPED1(ret_val, p, BADARG, ctx->bif,
+ ctx->arg[0]);
+ else
+ ERTS_BIF_PREP_ERROR_TRAPPED2(ret_val, p, BADARG, ctx->bif,
+ ctx->arg[0], ctx->arg[1]);
+ }
+ b2t_destroy_context(ctx);
+ return ret_val;
case B2TDone:
b2t_destroy_context(ctx);
@@ -1524,7 +1536,8 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con
erts_set_gc_state(p, 1);
}
BUMP_REDS(p, (initial_reds - ctx->reds) / B2T_BYTES_PER_REDUCTION);
- return ctx->u.dc.res;
+ ERTS_BIF_PREP_RET(ret_val, ctx->u.dc.res);
+ return ret_val;
default:
ASSERT(!"Unknown state in binary_to_term");
@@ -1541,15 +1554,24 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con
erts_set_gc_state(p, 0);
}
BUMP_ALL_REDS(p);
- BIF_TRAP1(&binary_to_term_trap_export, p, ctx->trap_bin);
+
+ ERTS_BIF_PREP_TRAP1(ret_val, &binary_to_term_trap_export,
+ p, ctx->trap_bin);
+
+ return ret_val;
}
-BIF_RETTYPE erts_internal_binary_to_term_1(BIF_ALIST_1)
+HIPE_WRAPPER_BIF_DISABLE_GC(binary_to_term, 1)
+
+BIF_RETTYPE binary_to_term_1(BIF_ALIST_1)
{
- return binary_to_term_int(BIF_P, 0, BIF_ARG_1, NULL);
+ return binary_to_term_int(BIF_P, 0, BIF_ARG_1, NULL, bif_export[BIF_binary_to_term_1],
+ BIF_ARG_1, THE_NON_VALUE);
}
-BIF_RETTYPE erts_internal_binary_to_term_2(BIF_ALIST_2)
+HIPE_WRAPPER_BIF_DISABLE_GC(binary_to_term, 2)
+
+BIF_RETTYPE binary_to_term_2(BIF_ALIST_2)
{
Eterm opts;
Eterm opt;
@@ -1570,7 +1592,8 @@ BIF_RETTYPE erts_internal_binary_to_term_2(BIF_ALIST_2)
if (is_not_nil(opts))
goto error;
- return binary_to_term_int(BIF_P, flags, BIF_ARG_1, NULL);
+ return binary_to_term_int(BIF_P, flags, BIF_ARG_1, NULL, bif_export[BIF_binary_to_term_2],
+ BIF_ARG_1, BIF_ARG_2);
error:
BIF_ERROR(BIF_P, BADARG);
@@ -2562,29 +2585,25 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
{
map_t *mp = (map_t*)map_val(obj);
Uint size = map_get_size(mp);
- Eterm *mptr;
*ep++ = MAP_EXT;
put_int32(size, ep); ep += 4;
- /* Push values first */
if (size > 0) {
- mptr = map_get_values(mp);
+ Eterm *kptr = map_get_keys(mp);
+ Eterm *vptr = map_get_values(mp);
+
for (i = size-1; i >= 1; i--) {
WSTACK_PUSH(s, ENC_TERM);
- WSTACK_PUSH(s, (UWord) mptr[i]);
+ WSTACK_PUSH(s, (UWord) vptr[i]);
+ WSTACK_PUSH(s, ENC_TERM);
+ WSTACK_PUSH(s, (UWord) kptr[i]);
}
WSTACK_PUSH(s, ENC_TERM);
- WSTACK_PUSH(s, (UWord) mptr[0]);
-
- mptr = map_get_keys(mp);
- for (i = size-1; i >= 1; i--) {
- WSTACK_PUSH(s, ENC_TERM);
- WSTACK_PUSH(s, (UWord) mptr[i]);
- }
+ WSTACK_PUSH(s, (UWord) vptr[0]);
- obj = mptr[0];
+ obj = kptr[0];
goto L_jump_start;
}
}
@@ -3518,16 +3537,16 @@ dec_term_atom_common:
keys = make_tuple(hp);
*hp++ = make_arityval(size);
- kptr = hp;
hp += size;
+ kptr = hp - 1;
mp = (map_t*)hp;
hp += MAP_HEADER_SIZE;
- vptr = hp;
hp += size;
+ vptr = hp - 1;
- /* kptr, first word for keys
- * vptr, first word for values
+ /* kptr, last word for keys
+ * vptr, last word for values
*/
/*
@@ -3542,27 +3561,12 @@ dec_term_atom_common:
mp->keys = keys;
*objp = make_map(mp);
- /* We assume the map is wellformed, meaning:
- * - ascending key order
- * - unique keys
- */
-
- objp = vptr + size - 1;
- n = size;
-
- while (n-- > 0) {
- *objp = (Eterm) COMPRESS_POINTER(next);
- next = objp;
- objp--;
- }
-
- objp = kptr + size - 1;
- n = size;
-
- while (n-- > 0) {
- *objp = (Eterm) COMPRESS_POINTER(next);
- next = objp;
- objp--;
+ for (n = size; n; n--) {
+ *vptr = (Eterm) COMPRESS_POINTER(next);
+ *kptr = (Eterm) COMPRESS_POINTER(vptr);
+ next = kptr;
+ vptr--;
+ kptr--;
}
}
break;
@@ -4459,66 +4463,3 @@ error:
#undef SKIP2
#undef CHKSIZE
}
-
-
-#ifdef HIPE
-BIF_RETTYPE hipe_wrapper_term_to_binary_1(BIF_ALIST_1);
-BIF_RETTYPE hipe_wrapper_term_to_binary_2(BIF_ALIST_2);
-BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_1(BIF_ALIST_1);
-BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_2(BIF_ALIST_2);
-
-/* Hipe wrappers used by native code for BIFs that disable GC while trapping.
- *
- * Problem:
- * When native code calls a BIF that traps, hipe_mode_switch will push a
- * "trap frame" on the Erlang stack in order to find its way back from beam_emu
- * back to native caller when finally done. If GC is disabled and stack/heap
- * is full there is no place to push the "trap frame".
- *
- * Solution:
- * We reserve space on stack for the "trap frame" here before the BIF is called.
- * If the BIF does not trap, the space is reclaimed here before returning.
- * If the BIF traps, hipe_push_beam_trap_frame() will detect that a "trap frame"
- * already is reserved and use it.
- */
-BIF_RETTYPE hipe_wrapper_term_to_binary_1(BIF_ALIST_1)
-{
- Eterm res;
- hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 1);
- res = term_to_binary_1(BIF_P, BIF__ARGS);
- if (is_value(res) || BIF_P->freason != TRAP) {
- hipe_unreserve_beam_trap_frame(BIF_P);
- }
- return res;
-}
-BIF_RETTYPE hipe_wrapper_term_to_binary_2(BIF_ALIST_2)
-{
- Eterm res;
- hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 2);
- res = term_to_binary_2(BIF_P, BIF__ARGS);
- if (is_value(res) || BIF_P->freason != TRAP) {
- hipe_unreserve_beam_trap_frame(BIF_P);
- }
- return res;
-}
-BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_1(BIF_ALIST_1)
-{
- Eterm res;
- hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 1);
- res = erts_internal_binary_to_term_1(BIF_P, BIF__ARGS);
- if (is_value(res) || BIF_P->freason != TRAP) {
- hipe_unreserve_beam_trap_frame(BIF_P);
- }
- return res;
-}
-BIF_RETTYPE hipe_wrapper_erts_internal_binary_to_term_2(BIF_ALIST_2)
-{
- Eterm res;
- hipe_reserve_beam_trap_frame(BIF_P, BIF__ARGS, 2);
- res = erts_internal_binary_to_term_2(BIF_P, BIF__ARGS);
- if (is_value(res) || BIF_P->freason != TRAP) {
- hipe_unreserve_beam_trap_frame(BIF_P);
- }
- return res;
-}
-#endif /*HIPE*/
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 8fcb95d0e2..891046a8b5 100755
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -435,6 +435,8 @@ do {\
}\
} while(0)
+#define CLEAR_SAVED_ESTACK(estack) ((void) ((estack)->start = NULL))
+
/*
* Use on empty stack, only the allocator can be changed before this.
* The src stack is reset to NULL.
@@ -551,6 +553,8 @@ do {\
}\
} while(0)
+#define CLEAR_SAVED_WSTACK(wstack) ((void) ((wstack)->wstart = NULL))
+
/*
* Use on empty stack, only the allocator can be changed before this.
* The src stack is reset to NULL.
@@ -951,20 +955,67 @@ struct Sint_buf {
};
char* Sint_to_buf(Sint, struct Sint_buf*);
+#define ERTS_IOLIST_STATE_INITER(C_P, OBJ) \
+ {(C_P), 0, 0, (OBJ), {NULL, NULL, NULL, ERTS_ALC_T_INVALID}, 0, 0}
+
+#define ERTS_IOLIST_STATE_MOVE(TO, FROM) \
+ sys_memcpy((void *) (TO), (void *) (FROM), sizeof(ErtsIOListState))
+
+#define ERTS_IOLIST_SIZE_YIELDS_COUNT_PER_RED 8
+
+typedef struct {
+ Process *c_p;
+ ErlDrvSizeT size;
+ Uint offs;
+ Eterm obj;
+ ErtsEStack estack;
+ int reds_left;
+ int have_size;
+} ErtsIOListState;
+
+#define ERTS_IOLIST2BUF_STATE_INITER(C_P, OBJ) \
+ {ERTS_IOLIST_STATE_INITER((C_P), (OBJ)), {NULL, 0, 0, 0}, NULL, 0, NULL, 0}
+
+#define ERTS_IOLIST2BUF_STATE_MOVE(TO, FROM) \
+ sys_memcpy((void *) (TO), (void *) (FROM), sizeof(ErtsIOList2BufState))
+
+#define ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT 32
+#define ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED 8
+#define ERTS_IOLIST_TO_BUF_BYTES_PER_RED \
+ (ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED*ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT)
+
+typedef struct {
+ ErtsIOListState iolist;
+ struct {
+ byte *bptr;
+ size_t size;
+ Uint bitoffs;
+ Uint bitsize;
+ } bcopy;
+ char *buf;
+ ErlDrvSizeT len;
+ Eterm *objp;
+ int offset;
+} ErtsIOList2BufState;
+
#define ERTS_IOLIST_OK 0
#define ERTS_IOLIST_OVERFLOW 1
#define ERTS_IOLIST_TYPE 2
+#define ERTS_IOLIST_YIELD 3
Eterm buf_to_intlist(Eterm**, const char*, size_t, Eterm); /* most callers pass plain char*'s */
#define ERTS_IOLIST_TO_BUF_OVERFLOW (~((ErlDrvSizeT) 0))
#define ERTS_IOLIST_TO_BUF_TYPE_ERROR (~((ErlDrvSizeT) 1))
+#define ERTS_IOLIST_TO_BUF_YIELD (~((ErlDrvSizeT) 2))
#define ERTS_IOLIST_TO_BUF_FAILED(R) \
- (((R) & (~((ErlDrvSizeT) 1))) == (~((ErlDrvSizeT) 1)))
+ (((R) & (~((ErlDrvSizeT) 3))) == (~((ErlDrvSizeT) 3)))
#define ERTS_IOLIST_TO_BUF_SUCCEEDED(R) \
(!ERTS_IOLIST_TO_BUF_FAILED((R)))
ErlDrvSizeT erts_iolist_to_buf(Eterm, char*, ErlDrvSizeT);
+ErlDrvSizeT erts_iolist_to_buf_yielding(ErtsIOList2BufState *);
+int erts_iolist_size_yielding(ErtsIOListState *state);
int erts_iolist_size(Eterm, ErlDrvSizeT *);
int is_string(Eterm);
void erl_at_exit(void (*) (void*), void*);
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index cd5060ebb3..edf4a28784 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -914,8 +914,8 @@ int erts_port_handle_xports(Port *prt)
(iov)->iov_base = (ptr); \
(iov)->iov_len = (len); \
if (sizeof((iov)->iov_len) < sizeof(len) \
- /* Check if (len) overflowed (iov)->iov_len */ \
- && ((len) >> (sizeof((iov)->iov_len)*CHAR_BIT)) != 0) { \
+ /* Check if (len) overflowed (iov)->iov_len */ \
+ && (iov)->iov_len != (len)) { \
goto L_overflow; \
} \
*(bv)++ = (bin); \
@@ -2473,7 +2473,7 @@ set_port_connected(int bang_op,
DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE);
dtrace_pid_str(connect, process_str);
- erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id);
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", prt->common.id);
dtrace_proc_str(rp, newprocess_str);
DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str);
}
@@ -3591,9 +3591,9 @@ erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed)
DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
DTRACE_CHARBUF(rreason_str, 64);
- erts_snprintf(from_str, sizeof(from_str), "%T", from);
+ erts_snprintf(from_str, sizeof(DTRACE_CHARBUF_NAME(from_str)), "%T", from);
dtrace_port_str(p, port_str);
- erts_snprintf(rreason_str, sizeof(rreason_str), "%T", rreason);
+ erts_snprintf(rreason_str, sizeof(DTRACE_CHARBUF_NAME(rreason_str)), "%T", rreason);
DTRACE4(port_exit, from_str, port_str, p->name, rreason_str);
}
#endif
@@ -4660,7 +4660,7 @@ set_busy_port(ErlDrvPort dprt, int on)
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(port_busy)) {
- erts_snprintf(port_str, sizeof(port_str),
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
"%T", prt->common.id);
DTRACE1(port_busy, port_str);
}
@@ -4673,7 +4673,7 @@ set_busy_port(ErlDrvPort dprt, int on)
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(port_not_busy)) {
- erts_snprintf(port_str, sizeof(port_str),
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)),
"%T", prt->common.id);
DTRACE1(port_not_busy, port_str);
}
@@ -4725,9 +4725,9 @@ erts_port_resume_procs(Port *prt)
DTRACE_CHARBUF(pid_str, 16);
ErtsProcList* plp2 = plp;
- erts_snprintf(port_str, sizeof(port_str), "%T", prt->common.id);
+ erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", prt->common.id);
while (plp2 != NULL) {
- erts_snprintf(pid_str, sizeof(pid_str), "%T", plp2->pid);
+ erts_snprintf(pid_str, sizeof(DTRACE_CHARBUF_NAME(pid_str)), "%T", plp2->pid);
DTRACE2(process_port_unblocked, pid_str, port_str);
}
}
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 73630fda8e..68fcc177ae 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -1484,7 +1484,8 @@ new_map j d I I
update_map_assoc j s d I I
update_map_exact j s d I I
-is_map Fail cq => jump Fail
+is_map Fail Literal=q => move Literal x | is_map Fail x
+is_map Fail c => jump Fail
%macro: is_map IsMap -fail_action
is_map f r
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index e273056a2b..05f07e57b2 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -154,10 +154,14 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
/* In VC++, noreturn is a declspec that has to be before the types,
* but in GNUC it is an att ribute to be placed between return type
* and function name, hence __decl_noreturn <types> __noreturn <function name>
+ *
+ * at some platforms (e.g. Android) __noreturn is defined at sys/cdef.h
*/
#if __GNUC__
# define __decl_noreturn
-# define __noreturn __attribute__((noreturn))
+# ifndef __noreturn
+# define __noreturn __attribute__((noreturn))
+# endif
#else
# if defined(__WIN32__) && defined(_MSC_VER)
# define __noreturn
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 5b43d25e3c..72092ec7b0 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -2125,7 +2125,11 @@ tailrecur_ne:
if (!is_boxed(b) || *boxed_val_rel(b,b_base) != *aa)
goto not_equal;
bb = map_val_rel(b,b_base);
- if ((sz = map_get_size((map_t*)aa)) == 0) goto pop_next;
+ sz = map_get_size((map_t*)aa);
+
+ if (sz != map_get_size((map_t*)bb)) goto not_equal;
+ if (sz == 0) goto pop_next;
+
aa += 2;
bb += 2;
sz += 1; /* increment for tuple-keys */
@@ -3193,106 +3197,303 @@ buf_to_intlist(Eterm** hpp, const char *buf, size_t len, Eterm tail)
**
*/
-ErlDrvSizeT erts_iolist_to_buf(Eterm obj, char* buf, ErlDrvSizeT alloced_len)
+typedef enum {
+ ERTS_IL2B_BCOPY_OK,
+ ERTS_IL2B_BCOPY_YIELD,
+ ERTS_IL2B_BCOPY_OVERFLOW,
+ ERTS_IL2B_BCOPY_TYPE_ERROR
+} ErtsIL2BBCopyRes;
+
+static ErtsIL2BBCopyRes
+iolist_to_buf_bcopy(ErtsIOList2BufState *state, Eterm obj, int *yield_countp);
+
+static ERTS_INLINE ErlDrvSizeT
+iolist_to_buf(const int yield_support,
+ ErtsIOList2BufState *state,
+ Eterm obj,
+ char* buf,
+ ErlDrvSizeT alloced_len)
{
- ErlDrvSizeT len = (ErlDrvSizeT) alloced_len;
- Eterm* objp;
+#undef IOLIST_TO_BUF_BCOPY
+#define IOLIST_TO_BUF_BCOPY(CONSP) \
+do { \
+ size_t size = binary_size(obj); \
+ if (size > 0) { \
+ Uint bitsize; \
+ byte* bptr; \
+ Uint bitoffs; \
+ Uint num_bits; \
+ if (yield_support) { \
+ size_t max_size = ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; \
+ if (yield_count > 0) \
+ max_size *= yield_count+1; \
+ if (size > max_size) { \
+ state->objp = CONSP; \
+ goto L_bcopy_yield; \
+ } \
+ if (size >= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT) { \
+ int cost = (int) size; \
+ cost /= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT; \
+ yield_count -= cost; \
+ } \
+ } \
+ if (len < size) \
+ goto L_overflow; \
+ ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize); \
+ if (bitsize != 0) \
+ goto L_type_error; \
+ num_bits = 8*size; \
+ copy_binary_to_buffer(buf, 0, bptr, bitoffs, num_bits); \
+ buf += size; \
+ len -= size; \
+ } \
+} while (0)
+
+ ErlDrvSizeT res, len;
+ Eterm* objp = NULL;
+ int init_yield_count;
+ int yield_count;
DECLARE_ESTACK(s);
- goto L_again;
-
- while (!ESTACK_ISEMPTY(s)) {
- obj = ESTACK_POP(s);
- L_again:
- if (is_list(obj)) {
- L_iter_list:
- objp = list_val(obj);
- obj = CAR(objp);
- if (is_byte(obj)) {
- if (len == 0) {
- goto L_overflow;
- }
- *buf++ = unsigned_val(obj);
- len--;
- } else if (is_binary(obj)) {
- byte* bptr;
- size_t size = binary_size(obj);
- Uint bitsize;
- Uint bitoffs;
- Uint num_bits;
-
- if (len < size) {
+
+ len = (ErlDrvSizeT) alloced_len;
+
+ if (!yield_support) {
+ yield_count = init_yield_count = 0; /* Shut up faulty warning... >:-( */
+ goto L_again;
+ }
+ else {
+
+ if (state->iolist.reds_left <= 0)
+ return ERTS_IOLIST_TO_BUF_YIELD;
+
+ ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
+ init_yield_count = (ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED
+ * state->iolist.reds_left);
+ yield_count = init_yield_count;
+
+ if (!state->iolist.estack.start)
+ goto L_again;
+ else {
+ int chk_stack;
+ /* Restart; restore state... */
+ ESTACK_RESTORE(s, &state->iolist.estack);
+
+ if (!state->bcopy.bptr)
+ chk_stack = 0;
+ else {
+ chk_stack = 1;
+ switch (iolist_to_buf_bcopy(state, THE_NON_VALUE, &yield_count)) {
+ case ERTS_IL2B_BCOPY_OK:
+ break;
+ case ERTS_IL2B_BCOPY_YIELD:
+ BUMP_ALL_REDS(state->iolist.c_p);
+ state->iolist.reds_left = 0;
+ ESTACK_SAVE(s, &state->iolist.estack);
+ return ERTS_IOLIST_TO_BUF_YIELD;
+ case ERTS_IL2B_BCOPY_OVERFLOW:
goto L_overflow;
- }
- ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize);
- if (bitsize != 0) {
+ case ERTS_IL2B_BCOPY_TYPE_ERROR:
goto L_type_error;
}
- num_bits = 8*size;
- copy_binary_to_buffer(buf, 0, bptr, bitoffs, num_bits);
- buf += size;
- len -= size;
- } else if (is_list(obj)) {
- ESTACK_PUSH(s, CDR(objp));
- goto L_iter_list; /* on head */
- } else if (is_not_nil(obj)) {
- goto L_type_error;
}
- obj = CDR(objp);
- if (is_list(obj)) {
- goto L_iter_list; /* on tail */
- } else if (is_binary(obj)) {
- byte* bptr;
- size_t size = binary_size(obj);
- Uint bitsize;
- Uint bitoffs;
- Uint num_bits;
- if (len < size) {
- goto L_overflow;
+ obj = state->iolist.obj;
+ buf = state->buf;
+ len = state->len;
+ objp = state->objp;
+ state->objp = NULL;
+ if (objp)
+ goto L_tail;
+ if (!chk_stack)
+ goto L_again;
+ /* check stack */
+ }
+ }
+
+ while (!ESTACK_ISEMPTY(s)) {
+ obj = ESTACK_POP(s);
+ L_again:
+ if (is_list(obj)) {
+ while (1) { /* Tail loop */
+ while (1) { /* Head loop */
+ if (yield_support && --yield_count <= 0)
+ goto L_yield;
+ objp = list_val(obj);
+ obj = CAR(objp);
+ if (is_byte(obj)) {
+ if (len == 0) {
+ goto L_overflow;
+ }
+ *buf++ = unsigned_val(obj);
+ len--;
+ } else if (is_binary(obj)) {
+ IOLIST_TO_BUF_BCOPY(objp);
+ } else if (is_list(obj)) {
+ ESTACK_PUSH(s, CDR(objp));
+ continue; /* Head loop */
+ } else if (is_not_nil(obj)) {
+ goto L_type_error;
+ }
+ break;
}
- ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize);
- if (bitsize != 0) {
+
+ L_tail:
+
+ obj = CDR(objp);
+
+ if (is_list(obj)) {
+ continue; /* Tail loop */
+ } else if (is_binary(obj)) {
+ IOLIST_TO_BUF_BCOPY(NULL);
+ } else if (is_not_nil(obj)) {
goto L_type_error;
}
- num_bits = 8*size;
- copy_binary_to_buffer(buf, 0, bptr, bitoffs, num_bits);
- buf += size;
- len -= size;
- } else if (is_not_nil(obj)) {
- goto L_type_error;
+ break;
}
} else if (is_binary(obj)) {
- byte* bptr;
- size_t size = binary_size(obj);
- Uint bitsize;
- Uint bitoffs;
- Uint num_bits;
- if (len < size) {
- goto L_overflow;
- }
- ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize);
- if (bitsize != 0) {
- goto L_type_error;
- }
- num_bits = 8*size;
- copy_binary_to_buffer(buf, 0, bptr, bitoffs, num_bits);
- buf += size;
- len -= size;
+ IOLIST_TO_BUF_BCOPY(NULL);
} else if (is_not_nil(obj)) {
goto L_type_error;
- }
+ } else if (yield_support && --yield_count <= 0)
+ goto L_yield;
}
+ res = len;
+
+ L_return:
+
DESTROY_ESTACK(s);
- return len;
+
+ if (yield_support) {
+ int reds;
+ CLEAR_SAVED_ESTACK(&state->iolist.estack);
+ reds = ((init_yield_count - yield_count - 1)
+ / ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED) + 1;
+ BUMP_REDS(state->iolist.c_p, reds);
+ state->iolist.reds_left -= reds;
+ if (state->iolist.reds_left < 0)
+ state->iolist.reds_left = 0;
+ }
+
+
+ return res;
L_type_error:
- DESTROY_ESTACK(s);
- return ERTS_IOLIST_TO_BUF_TYPE_ERROR;
+ res = ERTS_IOLIST_TO_BUF_TYPE_ERROR;
+ goto L_return;
L_overflow:
- DESTROY_ESTACK(s);
- return ERTS_IOLIST_TO_BUF_OVERFLOW;
+ res = ERTS_IOLIST_TO_BUF_OVERFLOW;
+ goto L_return;
+
+ L_bcopy_yield:
+
+ state->buf = buf;
+ state->len = len;
+
+ switch (iolist_to_buf_bcopy(state, obj, &yield_count)) {
+ case ERTS_IL2B_BCOPY_OK:
+ ERTS_INTERNAL_ERROR("Missing yield");
+ case ERTS_IL2B_BCOPY_YIELD:
+ BUMP_ALL_REDS(state->iolist.c_p);
+ state->iolist.reds_left = 0;
+ ESTACK_SAVE(s, &state->iolist.estack);
+ return ERTS_IOLIST_TO_BUF_YIELD;
+ case ERTS_IL2B_BCOPY_OVERFLOW:
+ goto L_overflow;
+ case ERTS_IL2B_BCOPY_TYPE_ERROR:
+ goto L_type_error;
+ }
+
+ L_yield:
+
+ BUMP_ALL_REDS(state->iolist.c_p);
+ state->iolist.reds_left = 0;
+ state->iolist.obj = obj;
+ state->buf = buf;
+ state->len = len;
+ ESTACK_SAVE(s, &state->iolist.estack);
+ return ERTS_IOLIST_TO_BUF_YIELD;
+
+#undef IOLIST_TO_BUF_BCOPY
+}
+
+static ErtsIL2BBCopyRes
+iolist_to_buf_bcopy(ErtsIOList2BufState *state, Eterm obj, int *yield_countp)
+{
+ ErtsIL2BBCopyRes res;
+ char *buf = state->buf;
+ ErlDrvSizeT len = state->len;
+ byte* bptr;
+ size_t size;
+ size_t max_size;
+ Uint bitoffs;
+ Uint num_bits;
+ int yield_count = *yield_countp;
+
+ if (state->bcopy.bptr) {
+ bptr = state->bcopy.bptr;
+ size = state->bcopy.size;
+ bitoffs = state->bcopy.bitoffs;
+ state->bcopy.bptr = NULL;
+ }
+ else {
+ Uint bitsize;
+
+ ASSERT(is_binary(obj));
+
+ size = binary_size(obj);
+ if (size <= 0)
+ return ERTS_IL2B_BCOPY_OK;
+
+ if (len < size)
+ return ERTS_IL2B_BCOPY_OVERFLOW;
+
+ ERTS_GET_BINARY_BYTES(obj, bptr, bitoffs, bitsize);
+ if (bitsize != 0)
+ return ERTS_IL2B_BCOPY_TYPE_ERROR;
+ }
+
+ ASSERT(size > 0);
+ max_size = (size_t) ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT;
+ if (yield_count > 0)
+ max_size *= (size_t) (yield_count+1);
+
+ if (size <= max_size) {
+ if (size >= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT) {
+ int cost = (int) size;
+ cost /= ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT;
+ yield_count -= cost;
+ }
+ res = ERTS_IL2B_BCOPY_OK;
+ }
+ else {
+ ASSERT(0 < max_size && max_size < size);
+ yield_count = 0;
+ state->bcopy.bptr = bptr + max_size;
+ state->bcopy.bitoffs = bitoffs;
+ state->bcopy.size = size - max_size;
+ size = max_size;
+ res = ERTS_IL2B_BCOPY_YIELD;
+ }
+
+ num_bits = 8*size;
+ copy_binary_to_buffer(buf, 0, bptr, bitoffs, num_bits);
+ state->buf += size;
+ state->len -= size;
+ *yield_countp = yield_count;
+
+ return res;
+}
+
+ErlDrvSizeT erts_iolist_to_buf_yielding(ErtsIOList2BufState *state)
+{
+ return iolist_to_buf(1, state, state->iolist.obj, state->buf, state->len);
+}
+
+ErlDrvSizeT erts_iolist_to_buf(Eterm obj, char* buf, ErlDrvSizeT alloced_len)
+{
+ return iolist_to_buf(0, NULL, obj, buf, alloced_len);
}
/*
@@ -3303,11 +3504,32 @@ ErlDrvSizeT erts_iolist_to_buf(Eterm obj, char* buf, ErlDrvSizeT alloced_len)
* Any input term error detected in erts_iolist_to_buf should also
* be detected in this function!
*/
-int erts_iolist_size(Eterm obj, ErlDrvSizeT* sizep)
+
+static ERTS_INLINE int
+iolist_size(const int yield_support, ErtsIOListState *state, Eterm obj, ErlDrvSizeT* sizep)
{
+ int res, init_yield_count, yield_count;
Eterm* objp;
- Uint size = 0; /* Intentionally Uint due to halfword heap */
+ Uint size = (Uint) *sizep; /* Intentionally Uint due to halfword heap */
DECLARE_ESTACK(s);
+
+ if (!yield_support)
+ yield_count = init_yield_count = 0; /* Shut up faulty warning... >:-( */
+ else {
+ if (state->reds_left <= 0)
+ return ERTS_IOLIST_YIELD;
+ ESTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
+ init_yield_count = ERTS_IOLIST_SIZE_YIELDS_COUNT_PER_RED;
+ init_yield_count *= state->reds_left;
+ yield_count = init_yield_count;
+ if (state->estack.start) {
+ /* Restart; restore state... */
+ ESTACK_RESTORE(s, &state->estack);
+ size = (Uint) state->size;
+ obj = state->obj;
+ }
+ }
+
goto L_again;
#define SAFE_ADD(Var, Val) \
@@ -3323,51 +3545,101 @@ int erts_iolist_size(Eterm obj, ErlDrvSizeT* sizep)
obj = ESTACK_POP(s);
L_again:
if (is_list(obj)) {
- L_iter_list:
- objp = list_val(obj);
- /* Head */
- obj = CAR(objp);
- if (is_byte(obj)) {
- size++;
- if (size == 0) {
- goto L_overflow_error;
+ while (1) { /* Tail loop */
+ while (1) { /* Head loop */
+ if (yield_support && --yield_count <= 0)
+ goto L_yield;
+ objp = list_val(obj);
+ /* Head */
+ obj = CAR(objp);
+ if (is_byte(obj)) {
+ size++;
+ if (size == 0) {
+ goto L_overflow_error;
+ }
+ } else if (is_binary(obj) && binary_bitsize(obj) == 0) {
+ SAFE_ADD(size, binary_size(obj));
+ } else if (is_list(obj)) {
+ ESTACK_PUSH(s, CDR(objp));
+ continue; /* Head loop */
+ } else if (is_not_nil(obj)) {
+ goto L_type_error;
+ }
+ break;
}
- } else if (is_binary(obj) && binary_bitsize(obj) == 0) {
- SAFE_ADD(size, binary_size(obj));
- } else if (is_list(obj)) {
- ESTACK_PUSH(s, CDR(objp));
- goto L_iter_list; /* on head */
- } else if (is_not_nil(obj)) {
- goto L_type_error;
+ /* Tail */
+ obj = CDR(objp);
+ if (is_list(obj))
+ continue; /* Tail loop */
+ else if (is_binary(obj) && binary_bitsize(obj) == 0) {
+ SAFE_ADD(size, binary_size(obj));
+ } else if (is_not_nil(obj)) {
+ goto L_type_error;
+ }
+ break;
}
- /* Tail */
- obj = CDR(objp);
- if (is_list(obj))
- goto L_iter_list; /* on tail */
- else if (is_binary(obj) && binary_bitsize(obj) == 0) {
+ } else {
+ if (yield_support && --yield_count <= 0)
+ goto L_yield;
+ if (is_binary(obj) && binary_bitsize(obj) == 0) { /* Tail was binary */
SAFE_ADD(size, binary_size(obj));
} else if (is_not_nil(obj)) {
goto L_type_error;
}
- } else if (is_binary(obj) && binary_bitsize(obj) == 0) { /* Tail was binary */
- SAFE_ADD(size, binary_size(obj));
- } else if (is_not_nil(obj)) {
- goto L_type_error;
}
}
#undef SAFE_ADD
- DESTROY_ESTACK(s);
*sizep = (ErlDrvSizeT) size;
- return ERTS_IOLIST_OK;
- L_overflow_error:
+ res = ERTS_IOLIST_OK;
+
+ L_return:
+
DESTROY_ESTACK(s);
- return ERTS_IOLIST_OVERFLOW;
+
+ if (yield_support) {
+ int yc, reds;
+ CLEAR_SAVED_ESTACK(&state->estack);
+ yc = init_yield_count - yield_count;
+ reds = ((yc - 1) / ERTS_IOLIST_SIZE_YIELDS_COUNT_PER_RED) + 1;
+ BUMP_REDS(state->c_p, reds);
+ state->reds_left -= reds;
+ state->size = (ErlDrvSizeT) size;
+ state->have_size = 1;
+ }
+
+ return res;
+
+ L_overflow_error:
+ res = ERTS_IOLIST_OVERFLOW;
+ size = 0;
+ goto L_return;
L_type_error:
- DESTROY_ESTACK(s);
- return ERTS_IOLIST_TYPE;
+ res = ERTS_IOLIST_TYPE;
+ size = 0;
+ goto L_return;
+
+ L_yield:
+ BUMP_ALL_REDS(state->c_p);
+ state->reds_left = 0;
+ state->size = size;
+ state->obj = obj;
+ ESTACK_SAVE(s, &state->estack);
+ return ERTS_IOLIST_YIELD;
+}
+
+int erts_iolist_size_yielding(ErtsIOListState *state)
+{
+ ErlDrvSizeT size = state->size;
+ return iolist_size(1, state, state->obj, &size);
+}
+
+int erts_iolist_size(Eterm obj, ErlDrvSizeT* sizep)
+{
+ *sizep = 0;
+ return iolist_size(0, NULL, obj, sizep);
}
/* return 0 if item is not a non-empty flat list of bytes */
diff --git a/erts/emulator/drivers/common/gzio.c b/erts/emulator/drivers/common/gzio.c
index ef539f8f9b..1ef1602ec9 100644
--- a/erts/emulator/drivers/common/gzio.c
+++ b/erts/emulator/drivers/common/gzio.c
@@ -230,6 +230,7 @@ local ErtsGzFile gz_open (path, mode)
errno = 0;
#if defined(FILENAMES_16BIT)
{
+ FILE* efile_wfopen(const WCHAR* name, const WCHAR* mode);
WCHAR wfmode[80];
int i = 0;
int j;
@@ -237,7 +238,7 @@ local ErtsGzFile gz_open (path, mode)
wfmode[i++] = (WCHAR) fmode[j];
}
wfmode[i++] = L'\0';
- s->file = _wfopen((WCHAR *)path, wfmode);
+ s->file = efile_wfopen((WCHAR *)path, wfmode);
if (s->file == NULL) {
return s->destroy(s), (ErtsGzFile)Z_NULL;
}
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 357a4b7bcb..09bada457d 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -93,6 +93,10 @@ typedef unsigned long long llu_t;
#define INT16_MAX (32767)
#endif
+#ifdef __OSE__
+#include "inet.h"
+#endif
+
#ifdef __WIN32__
#define STRNCASECMP strncasecmp
@@ -121,10 +125,6 @@ typedef unsigned long long llu_t;
#undef WANT_NONBLOCKING
#include "sys.h"
-#ifdef __OSE__
-#include "inet.h"
-#endif
-
#undef EWOULDBLOCK
#undef ETIMEDOUT
@@ -200,6 +200,7 @@ typedef unsigned long long llu_t;
#define HAVE_MULTICAST_SUPPORT
+#define HAVE_UDP
#define ERRNO_BLOCK WSAEWOULDBLOCK
@@ -294,29 +295,55 @@ static unsigned long zero_value = 0;
static unsigned long one_value = 1;
#elif defined (__OSE__)
+
+/*
+ * Some notes about how inet (currently only tcp) works on OSE.
+ * The driver uses OSE signals to communicate with the one_inet
+ * process. Because of the difference in how signals and file descriptors
+ * work the whole select/deselect mechanic is very different.
+ * In ose when a sock_select is done a function is called. That function
+ * notes the changes that the driver want to do, but does not act on it.
+ * later when the function returns the new desired state is compared
+ * to the previous state and the apprioriate actions are taken. The action
+ * is usually to either request more data from the stack or stop requesting
+ * data.
+ *
+ * One thing to note is that the driver never does select/deselect. It always
+ * listens for the signals. Flow of data is regulated by sending or not sending
+ * signals to the ose inet process.
+ *
+ * The interesting functions to look at are:
+ * * inet_driver_select : called when sock_select is called
+ * * tcp_inet_ose_dispatch_signal : checks state changes and sends new signals
+ * * tcp_inet_drv_output_ose : ready output callback, reads signals and calls
+ * dispatch_signal
+ * * tcp_inet_drv_input_ose : ready input callback.
+ */
+
+#include "efs.h"
#include "sys/socket.h"
#include "sys/uio.h"
#include "sfk/sys/sfk_uio.h"
#include "netinet/in.h"
#include "netinet/tcp.h"
#include "netdb.h"
+#include "ose_spi/socket.sig"
-ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
-{
- return 0;
-}
+
+static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int max_sz);
#define INVALID_SOCKET -1
#define INVALID_EVENT -1
#define SOCKET_ERROR -1
#define SOCKET int
-#define HANDLE long int
-#define FD_READ ERL_DRV_READ
-#define FD_WRITE ERL_DRV_WRITE
-#define FD_CLOSE 0
-#define FD_CONNECT ERL_DRV_WRITE
-#define FD_ACCEPT ERL_DRV_READ
+#define HANDLE int
+#define FD_READ ERL_DRV_READ
+#define FD_WRITE ERL_DRV_WRITE
+#define FD_CLOSE 0
+#define FD_CONNECT (1<<4)
+#define FD_ACCEPT (1<<5)
+#define SOCK_FD_ERROR (1<<6)
#define sock_connect(s, addr, len) connect((s), (addr), (len))
#define sock_listen(s, b) listen((s), (b))
@@ -335,11 +362,12 @@ ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
#define sock_sendto(s,buf,blen,flag,addr,alen) \
sendto((s),(buf),(blen),(flag),(addr),(alen))
#define sock_sendv(s, vec, size, np, flag) \
- (*(np) = writev((s), (struct iovec*)(vec), (size)))
+ (*(np) = writev_fallback((s), (struct iovec*)(vec), (size), (*(np))))
#define sock_sendmsg(s,msghdr,flag) sendmsg((s),(msghdr),(flag))
#define sock_open(af, type, proto) socket((af), (type), (proto))
#define sock_close(s) close((s))
+#define sock_dup(s) dup((s))
#define sock_shutdown(s, how) shutdown((s), (how))
#define sock_hostname(buf, len) gethostname((buf), (len))
@@ -355,19 +383,6 @@ ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
#define sock_create_event(d) ((d)->s) /* return file descriptor */
#define sock_close_event(e) /* do nothing */
-#define inet_driver_select(port, e, mode, on) \
- driver_select(port, e, mode | (on?ERL_DRV_USE:0), on)
-
-#define sock_select(d, flags, onoff) do { \
- ASSERT(!(d)->is_ignored); \
- (d)->event_mask = (onoff) ? \
- ((d)->event_mask | (flags)) : \
- ((d)->event_mask & ~(flags)); \
- DEBUGF(("sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX\r\n", \
- (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask)); \
- inet_driver_select((d)->port, (ErlDrvEvent)(long)(d)->event, (flags), (onoff)); \
- } while(0)
-
#ifndef WANT_NONBLOCKING
#define WANT_NONBLOCKING
#endif
@@ -397,6 +412,16 @@ typedef unsigned long u_long;
#define IN_EXPERIMENTAL(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xe0000000)
#define IN_BADCLASS(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xf0000000)
+#define sock_select(d, flags, onoff) do { \
+ ASSERT(!(d)->is_ignored); \
+ (d)->event_mask = (onoff) ? \
+ ((d)->event_mask | (flags)) : \
+ ((d)->event_mask & ~(flags)); \
+ DEBUGF(("(%s / %d) sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX, s=%d\r\n", \
+ __FILE__, __LINE__, (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask, (d)->s)); \
+ inet_driver_select((d), (flags), (onoff)); \
+ } while(0)
+
#else /* !__OSE__ && !__WIN32__ */
#include <sys/time.h>
@@ -438,6 +463,8 @@ typedef unsigned long u_long;
#include <setns.h>
#endif
+#define HAVE_UDP
+
/* SCTP support -- currently for UNIX platforms only: */
#undef HAVE_SCTP
#if defined(HAVE_SCTP_H)
@@ -660,13 +687,13 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
(d)->event_mask = (onoff) ? \
((d)->event_mask | (flags)) : \
((d)->event_mask & ~(flags)); \
- DEBUGF(("sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX\r\n", \
- (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask)); \
+ DEBUGF(("(%s / %d) sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX\r\n", \
+ __FILE__, __LINE__, (long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask)); \
inet_driver_select((d)->port, (ErlDrvEvent)(long)(d)->event, (flags), (onoff)); \
} while(0)
-#endif /* #ifdef __WIN32__ #else */
+#endif /* !__WIN32__ && !__OSE__ */
#ifdef HAVE_SOCKLEN_T
# define SOCKLEN_T socklen_t
@@ -1119,6 +1146,13 @@ typedef struct {
char *netns; /* Socket network namespace name
as full file path */
#endif
+#ifdef __OSE__
+ int select_state; /* state to keep track of whether we
+ should trigger another read/write
+ request at end of ready_input/output */
+ ErlDrvEvent events[6];
+#endif
+
} inet_descriptor;
@@ -1134,8 +1168,10 @@ static void tcp_inet_stop(ErlDrvData);
static void tcp_inet_command(ErlDrvData, char*, ErlDrvSizeT);
static void tcp_inet_commandv(ErlDrvData, ErlIOVec*);
static void tcp_inet_flush(ErlDrvData drv_data);
+#ifndef __OSE__
static void tcp_inet_drv_input(ErlDrvData, ErlDrvEvent);
static void tcp_inet_drv_output(ErlDrvData data, ErlDrvEvent event);
+#endif
static ErlDrvData tcp_inet_start(ErlDrvPort, char* command);
static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData, unsigned int,
char*, ErlDrvSizeT, char**, ErlDrvSizeT);
@@ -1147,7 +1183,72 @@ static void tcp_inet_event(ErlDrvData, ErlDrvEvent);
static void find_dynamic_functions(void);
#endif
-static struct erl_drv_entry tcp_inet_driver_entry =
+#ifdef __OSE__
+/* The structure of the signal used for requesting asynchronous
+ * notification from the stack. Under normal circumstances the network stack
+ * shouldn't overwrite the value set in the fd field by the sender
+ * of the request */
+struct OseAsyncSig {
+ struct FmEvent event;
+ int fd;
+};
+
+union SIGNAL {
+ SIGSELECT signo;
+ struct OseAsyncSig async;
+};
+
+static ErlDrvSSizeT tcp_inet_ctl_ose(ErlDrvData e, unsigned int cmd,
+ char* buf, ErlDrvSizeT len,
+ char** rbuf, ErlDrvSizeT rsize);
+static void tcp_inet_commandv_ose(ErlDrvData e, ErlIOVec* ev);
+static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event);
+static void tcp_inet_drv_input_ose(ErlDrvData data, ErlDrvEvent event);
+static ErlDrvOseEventId inet_resolve_signal(union SIGNAL *sig);
+
+#ifdef INET_DRV_DEBUG
+
+static char *read_req = "SO_EVENT_READ_REQUEST";
+static char *read_rep = "SO_EVENT_READ_REPLY";
+static char *write_req = "SO_EVENT_WRITE_REQUEST";
+static char *write_rep = "SO_EVENT_WRITE_REPLY";
+static char *eof_req = "SO_EVENT_EOF_REQUEST";
+static char *eof_rep = "SO_EVENT_EOF_REPLY";
+static char *accept_req = "SO_EVENT_ACCEPT_REQUEST";
+static char *accept_rep = "SO_EVENT_ACCEPT_REPLY";
+static char *connect_req = "SO_EVENT_CONNECT_REQUEST";
+static char *connect_rep = "SO_EVENT_CONNECT_REPLY";
+static char *error_req = "SO_EVENT_ERROR_REQUEST";
+static char *error_rep = "SO_EVENT_ERROR_REPLY";
+static char signo_tmp[32];
+
+static char *signo_to_string(SIGSELECT signo) {
+ switch (signo) {
+ case SO_EVENT_READ_REQUEST: { return read_req; }
+ case SO_EVENT_READ_REPLY: { return read_rep; }
+ case SO_EVENT_WRITE_REQUEST: { return write_req; }
+ case SO_EVENT_WRITE_REPLY: { return write_rep; }
+ case SO_EVENT_EOF_REQUEST: { return eof_req; }
+ case SO_EVENT_EOF_REPLY: { return eof_rep; }
+ case SO_EVENT_ACCEPT_REQUEST: { return accept_req; }
+ case SO_EVENT_ACCEPT_REPLY: { return accept_rep; }
+ case SO_EVENT_CONNECT_REQUEST: { return connect_req; }
+ case SO_EVENT_CONNECT_REPLY: { return connect_rep; }
+ case SO_EVENT_ERROR_REQUEST: { return error_req; }
+ case SO_EVENT_ERROR_REPLY: { return error_rep; }
+ }
+
+ snprintf(signo_tmp,32,"0x%x",signo);
+
+ return signo_tmp;
+}
+
+#endif
+
+#endif /* __OSE__ */
+
+
+static struct erl_drv_entry tcp_inet_driver_entry =
{
tcp_inet_init, /* inet_init will add this driver !! */
tcp_inet_start,
@@ -1156,6 +1257,9 @@ static struct erl_drv_entry tcp_inet_driver_entry =
#ifdef __WIN32__
tcp_inet_event,
NULL,
+#elif defined(__OSE__)
+ tcp_inet_drv_input_ose, /*ready_input*/
+ tcp_inet_drv_output_ose, /*ready_output*/
#else
tcp_inet_drv_input,
tcp_inet_drv_output,
@@ -1163,9 +1267,17 @@ static struct erl_drv_entry tcp_inet_driver_entry =
"tcp_inet",
NULL,
NULL,
+#ifdef __OSE__
+ tcp_inet_ctl_ose,
+#else
tcp_inet_ctl,
+#endif
tcp_inet_timeout,
+#ifdef __OSE__
+ tcp_inet_commandv_ose,
+#else
tcp_inet_commandv,
+#endif
NULL,
tcp_inet_flush,
NULL,
@@ -1181,6 +1293,7 @@ static struct erl_drv_entry tcp_inet_driver_entry =
+#ifdef HAVE_UDP
static int packet_inet_init(void);
static void packet_inet_stop(ErlDrvData);
static void packet_inet_command(ErlDrvData, char*, ErlDrvSizeT);
@@ -1230,6 +1343,7 @@ static struct erl_drv_entry udp_inet_driver_entry =
NULL,
inet_stop_select
};
+#endif
#ifdef HAVE_SCTP
static struct erl_drv_entry sctp_inet_driver_entry =
@@ -1293,6 +1407,7 @@ static int tcp_deliver(tcp_descriptor* desc, int len);
static int tcp_inet_output(tcp_descriptor* desc, HANDLE event);
static int tcp_inet_input(tcp_descriptor* desc, HANDLE event);
+#ifdef HAVE_UDP
typedef struct {
inet_descriptor inet; /* common data structure (DON'T MOVE) */
int read_packets; /* Number of packets to read per invocation */
@@ -1304,10 +1419,19 @@ typedef struct {
static int packet_inet_input(udp_descriptor* udesc, HANDLE event);
static int packet_inet_output(udp_descriptor* udesc, HANDLE event);
+#endif
/* convert descriptor poiner to inet_descriptor pointer */
#define INETP(d) (&(d)->inet)
+#ifdef __OSE__
+static void inet_driver_select(inet_descriptor* desc,
+ int flags, int onoff);
+static void tcp_inet_ose_dispatch_signals(tcp_descriptor *desc,
+ int prev_select_state,
+ union SIGNAL *sig);
+#endif
+
static int async_ref = 0; /* async reference id generator */
#define NEW_ASYNC_ID() ((async_ref++) & 0xffff)
@@ -1324,7 +1448,6 @@ static int async_ref = 0; /* async reference id generator */
static ErlDrvTermData am_ok;
static ErlDrvTermData am_tcp;
-static ErlDrvTermData am_udp;
static ErlDrvTermData am_error;
static ErlDrvTermData am_einval;
static ErlDrvTermData am_inet_async;
@@ -1334,10 +1457,13 @@ static ErlDrvTermData am_closed;
static ErlDrvTermData am_tcp_passive;
static ErlDrvTermData am_tcp_closed;
static ErlDrvTermData am_tcp_error;
-static ErlDrvTermData am_udp_passive;
-static ErlDrvTermData am_udp_error;
static ErlDrvTermData am_empty_out_q;
static ErlDrvTermData am_ssl_tls;
+#ifdef HAVE_UDP
+static ErlDrvTermData am_udp;
+static ErlDrvTermData am_udp_passive;
+static ErlDrvTermData am_udp_error;
+#endif
#ifdef HAVE_SCTP
static ErlDrvTermData am_sctp;
static ErlDrvTermData am_sctp_passive;
@@ -1545,6 +1671,7 @@ static void *realloc_wrapper(void *current, ErlDrvSizeT size){
# define SCTP_ANC_BUFF_SIZE INET_DEF_BUFFER/2 /* XXX: not very good... */
#endif
+#ifdef HAVE_UDP
static int load_ip_port(ErlDrvTermData* spec, int i, char* buf)
{
spec[i++] = ERL_DRV_INT;
@@ -1579,6 +1706,7 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf)
}
return i;
}
+#endif
#ifdef HAVE_SCTP
@@ -1745,10 +1873,12 @@ static void release_buffer(ErlDrvBinary* buf)
}
}
+#ifdef HAVE_UDP
static ErlDrvBinary* realloc_buffer(ErlDrvBinary* buf, ErlDrvSizeT newsz)
{
return driver_realloc_binary(buf, newsz);
}
+#endif
/* use a TRICK, access the refc field to see if any one else has
* a ref to this buffer then call driver_free_binary else
@@ -3409,6 +3539,7 @@ static int tcp_error_message(tcp_descriptor* desc, int err)
return erl_drv_output_term(desc->inet.dport, spec, i);
}
+#ifdef HAVE_UDP
/*
** active mode message:
** {udp, S, IP, Port, [H1,...Hsz | Data]} or
@@ -3499,6 +3630,7 @@ static int packet_binary_message
ASSERT(i <= PACKET_ERL_DRV_TERM_DATA_LEN);
return erl_drv_output_term(desc->dport, spec, i);
}
+#endif
/*
** active mode message: send active-to-passive transition message
@@ -3513,6 +3645,9 @@ static int packet_binary_message
DEBUGF(("packet_passive_message(%ld):\r\n", (long)desc->port));
+#if !defined(HAVE_UDP) && !defined(HAVE_SCTP)
+ i = LOAD_ATOM(spec, i, am_tcp_passive);
+#else
if (desc->sprotocol == IPPROTO_TCP)
i = LOAD_ATOM(spec, i, am_tcp_passive);
else {
@@ -3522,12 +3657,14 @@ static int packet_binary_message
i = LOAD_ATOM(spec, i, am_udp_passive);
#endif
}
+#endif
i = LOAD_PORT(spec, i, desc->dport);
i = LOAD_TUPLE(spec, i, 2);
ASSERT(i <= 6);
return erl_drv_output_term(desc->dport, spec, i);
}
+#ifdef HAVE_UDP
/*
** send active message {udp_error|sctp_error, S, Error}
*/
@@ -3554,7 +3691,7 @@ static int packet_error_message(udp_descriptor* udesc, int err)
ASSERT(i == sizeof(spec)/sizeof(*spec));
return erl_drv_output_term(desc->dport, spec, i);
}
-
+#endif
/*
** active=TRUE:
@@ -3619,7 +3756,7 @@ tcp_reply_binary_data(tcp_descriptor* desc, ErlDrvBinary* bin, int offs, int len
return code;
}
-
+#ifdef HAVE_UDP
static int
packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz,
ErlDrvBinary * bin, int offs, int len,
@@ -3642,6 +3779,7 @@ packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz,
return code;
}
}
+#endif
/* ----------------------------------------------------------------------------
@@ -3817,7 +3955,9 @@ static int inet_init()
INIT_ATOM(ok);
INIT_ATOM(tcp);
+#ifdef HAVE_UDP
INIT_ATOM(udp);
+#endif
INIT_ATOM(error);
INIT_ATOM(einval);
INIT_ATOM(inet_async);
@@ -3827,8 +3967,10 @@ static int inet_init()
INIT_ATOM(tcp_passive);
INIT_ATOM(tcp_closed);
INIT_ATOM(tcp_error);
+#ifdef HAVE_UDP
INIT_ATOM(udp_passive);
INIT_ATOM(udp_error);
+#endif
INIT_ATOM(empty_out_q);
INIT_ATOM(ssl_tls);
@@ -3847,7 +3989,10 @@ static int inet_init()
/* add TCP, UDP and SCTP drivers */
add_driver_entry(&tcp_inet_driver_entry);
+#ifdef HAVE_UDP
add_driver_entry(&udp_inet_driver_entry);
+#endif
+
#ifdef HAVE_SCTP
/* Check the size of SCTP AssocID -- currently both this driver and the
Erlang part require 32 bit: */
@@ -4176,6 +4321,16 @@ static void desc_close(inet_descriptor* desc)
desc->forced_events = 0;
desc->send_would_block = 0;
#endif
+#ifdef __OSE__
+ if (desc->events[0]) {
+ driver_select(desc->port,desc->events[0],FD_READ|FD_WRITE|ERL_DRV_USE,0);
+ driver_select(desc->port,desc->events[1],FD_READ|FD_WRITE|ERL_DRV_USE,0);
+ driver_select(desc->port,desc->events[2],FD_READ|FD_WRITE|ERL_DRV_USE,0);
+ driver_select(desc->port,desc->events[3],FD_READ|FD_WRITE|ERL_DRV_USE,0);
+ driver_select(desc->port,desc->events[4],FD_READ|FD_WRITE|ERL_DRV_USE,0);
+ driver_select(desc->port,desc->events[5],FD_READ|FD_WRITE|ERL_DRV_USE,0);
+ }
+#else
/*
* We should close the fd here, but the other driver might still
* be selecting on it.
@@ -4185,6 +4340,7 @@ static void desc_close(inet_descriptor* desc)
ERL_DRV_USE, 0);
else
inet_stop_select((ErlDrvEvent)(long)desc->event,NULL);
+#endif
desc->event = INVALID_EVENT; /* closed by stop_select callback */
desc->s = INVALID_SOCKET;
desc->event_mask = 0;
@@ -4226,6 +4382,64 @@ static int erl_inet_close(inet_descriptor* desc)
return 0;
}
+#ifdef __OSE__
+static void inet_select_init(inet_descriptor* desc)
+{
+ desc->events[0] =
+ erl_drv_ose_event_alloc(SO_EVENT_READ_REPLY,
+ desc->s,
+ inet_resolve_signal,
+ NULL);
+ driver_select(desc->port, desc->events[0],
+ ERL_DRV_READ|ERL_DRV_USE, 1);
+
+ desc->events[1] =
+ erl_drv_ose_event_alloc(SO_EVENT_EOF_REPLY,
+ desc->s,
+ inet_resolve_signal,
+ NULL);
+ driver_select(desc->port, desc->events[1],
+ ERL_DRV_READ|ERL_DRV_USE, 1);
+
+ desc->events[2] =
+ erl_drv_ose_event_alloc(SO_EVENT_ACCEPT_REPLY,
+ desc->s,
+ inet_resolve_signal,
+ NULL);
+ driver_select(desc->port, desc->events[2],
+ ERL_DRV_READ|ERL_DRV_USE, 1);
+
+ /* trigger tcp_inet_input */
+ desc->events[3] =
+ erl_drv_ose_event_alloc(SO_EVENT_WRITE_REPLY,
+ desc->s,
+ inet_resolve_signal,
+ NULL);
+ driver_select(desc->port, desc->events[3],
+ ERL_DRV_WRITE|ERL_DRV_USE, 1);
+
+ desc->events[4] =
+ erl_drv_ose_event_alloc(SO_EVENT_CONNECT_REPLY,
+ desc->s,
+ inet_resolve_signal,
+ NULL);
+ driver_select(desc->port, desc->events[4],
+ ERL_DRV_WRITE|ERL_DRV_USE, 1);
+
+ desc->events[5] =
+ erl_drv_ose_event_alloc(SO_EVENT_ERROR_REPLY,
+ desc->s,
+ inet_resolve_signal,
+ NULL);
+ driver_select(desc->port, desc->events[5],
+ ERL_DRV_WRITE|ERL_DRV_USE, 1);
+
+ /* Issue a select on error event before any other select to be sure we are
+ prepared to receive error notifications from the stack, even in the
+ situations when select isn't issued */
+ sock_select(desc, SOCK_FD_ERROR, 1);
+}
+#endif
static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type,
char** rbuf, ErlDrvSizeT rsize)
@@ -4309,6 +4523,10 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type,
#ifdef __WIN32__
driver_select(desc->port, desc->event, ERL_DRV_READ, 1);
#endif
+#ifdef __OSE__
+ inet_select_init(desc);
+#endif
+
desc->state = INET_STATE_OPEN;
desc->stype = type;
desc->sfamily = domain;
@@ -4328,7 +4546,14 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type,
return ctl_error(sock_errno(), rbuf, rsize);
if (name.sa.sa_family != domain)
return ctl_error(EINVAL, rbuf, rsize);
+#ifdef __OSE__
+ /* for fdopen duplicating the sd will allow to uniquely identify
+ the signal from OSE with erlang port */
+ desc->s = sock_dup(s);
+#else
desc->s = s;
+#endif
+
if ((desc->event = sock_create_event(desc)) == INVALID_EVENT)
return ctl_error(sock_errno(), rbuf, rsize);
SET_NONBLOCKING(desc->s);
@@ -4338,8 +4563,15 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type,
desc->state = INET_STATE_BOUND; /* assume bound */
if (type == SOCK_STREAM) { /* check if connected */
sz = sizeof(name);
- if (!IS_SOCKET_ERROR(sock_peer(s, (struct sockaddr*) &name, &sz)))
+ if (!IS_SOCKET_ERROR(sock_peer(s, (struct sockaddr*) &name, &sz))) {
desc->state = INET_STATE_CONNECTED;
+#ifdef __OSE__
+ /* since we are dealing with different descriptors (i.e. inet and
+ socket) the select part should be initialized with the right
+ values */
+ inet_select_init(desc);
+#endif
+ }
}
desc->prebound = 1; /* used to prevent a real close since
@@ -4365,8 +4597,7 @@ struct addr_if {
#ifndef SIOCGIFNETMASK
-static struct in_addr net_mask(in)
-struct in_addr in;
+static struct in_addr net_mask(struct in_addr in)
{
register u_long i = sock_ntohl(in.s_addr);
@@ -5929,6 +6160,7 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
}
continue;
+#ifdef HAVE_UDP
case INET_LOPT_UDP_READ_PACKETS:
if (desc->stype == SOCK_DGRAM) {
udp_descriptor* udesc = (udp_descriptor*) desc;
@@ -5936,6 +6168,7 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
udesc->read_packets = ival;
}
continue;
+#endif
#ifdef HAVE_SETNS
case INET_LOPT_NETNS:
@@ -6902,6 +7135,7 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
}
continue;
+#ifdef HAVE_UDP
case INET_LOPT_UDP_READ_PACKETS:
if (desc->stype == SOCK_DGRAM) {
*ptr++ = opt;
@@ -6911,6 +7145,7 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
TRUNCATE_TO(0,ptr);
}
continue;
+#endif
#ifdef HAVE_SETNS
case INET_LOPT_NETNS:
@@ -8041,6 +8276,15 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)
#ifdef HAVE_SETNS
desc->netns = NULL;
#endif
+#ifdef __OSE__
+ desc->select_state = 0;
+ desc->events[0] = NULL;
+ desc->events[1] = NULL;
+ desc->events[2] = NULL;
+ desc->events[3] = NULL;
+ desc->events[4] = NULL;
+ desc->events[5] = NULL;
+#endif
return (ErlDrvData)desc;
}
@@ -8761,6 +9005,11 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s,
copy_desc->inet.port = port;
copy_desc->inet.dport = driver_mk_port(port);
+
+#ifdef __OSE__
+ inet_select_init(&copy_desc->inet);
+#endif
+
*err = 0;
return copy_desc;
}
@@ -8822,8 +9071,22 @@ static void tcp_inet_stop(ErlDrvData e)
inet_stop(INETP(desc));
}
+#ifdef __OSE__
-
+static ErlDrvSSizeT tcp_inet_ctl_ose(ErlDrvData e, unsigned int cmd,
+ char* buf, ErlDrvSizeT len,
+ char** rbuf, ErlDrvSizeT rsize) {
+
+ tcp_descriptor* desc = (tcp_descriptor*)e;
+ int prev_select_state = INETP(desc)->select_state;
+
+ ErlDrvSSizeT res = tcp_inet_ctl(e,cmd,buf,len,rbuf,rsize);
+
+ tcp_inet_ose_dispatch_signals((tcp_descriptor*)e,prev_select_state,NULL);
+
+ return res;
+}
+#endif
/* TCP requests from Erlang */
static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
@@ -9039,7 +9302,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
ErlDrvTermData caller = driver_caller(desc->inet.port);
tcp_descriptor* accept_desc;
int err;
-
+
if ((accept_desc = tcp_inet_copy(desc,s,caller,&err)) == NULL) {
sock_close(s);
return ctl_error(err, rbuf, rsize);
@@ -9073,7 +9336,8 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
char tbuf[2];
int n;
- DEBUGF(("tcp_inet_ctl(%ld): RECV\r\n", (long)desc->inet.port));
+ DEBUGF(("tcp_inet_ctl(%ld): RECV (s=%d)\r\n",
+ (long)desc->inet.port, desc->inet.s));
/* INPUT: Timeout(4), Length(4) */
if (!IS_CONNECTED(INETP(desc))) {
if (desc->tcp_add_flags & TCP_ADDF_DELAYED_CLOSE_RECV) {
@@ -9245,6 +9509,16 @@ static void tcp_inet_command(ErlDrvData e, char *buf, ErlDrvSizeT len)
DEBUGF(("tcp_inet_command(%ld) }\r\n", (long)desc->inet.port));
}
+#ifdef __OSE__
+
+static void tcp_inet_commandv_ose(ErlDrvData e, ErlIOVec* ev) {
+ int prev_select_state = INETP((tcp_descriptor*)e)->select_state;
+ tcp_inet_commandv(e, ev);
+ tcp_inet_ose_dispatch_signals((tcp_descriptor*)e,prev_select_state,NULL);
+}
+
+#endif
+
static void tcp_inet_commandv(ErlDrvData e, ErlIOVec* ev)
{
@@ -9308,6 +9582,22 @@ static void inet_stop_select(ErlDrvEvent event, void* _)
{
#ifdef __WIN32__
WSACloseEvent((HANDLE)event);
+#elif defined(__OSE__)
+ ErlDrvOseEventId id;
+ union SIGNAL *sig;
+ erl_drv_ose_event_fetch(event, NULL, &id,NULL);
+ DEBUGF(("inet_stop_select(?#?) {s=%d\n",id));
+ sock_close((int)id);
+ /* On socket close all the signals waiting to be processed as part of the
+ select should be deallocated */
+ while((sig = erl_drv_ose_get_signal(event))) {
+ DEBUGF(("inet_stop_select(?#?): Freeing signal %s\n",
+ signo_to_string(sig->signo)));
+ free_buf(&sig);
+ }
+ erl_drv_ose_event_free(event);
+ DEBUGF(("inet_stop_select(?#?) }\n"));
+
#else
sock_close((SOCKET)(long)event);
#endif
@@ -9427,12 +9717,13 @@ static int tcp_remain(tcp_descriptor* desc, int* len)
int n = desc->i_ptr - ptr; /* number of bytes read */
int tlen;
- DEBUGF(("tcp_remain(%ld): s=%d, n=%d, nfill=%d nsz=%d\r\n",
- (long)desc->inet.port, desc->inet.s, n, nfill, nsz));
-
tlen = packet_get_length(desc->inet.htype, ptr, n,
desc->inet.psize, desc->i_bufsz,
&desc->http_state);
+
+ DEBUGF(("tcp_remain(%ld): s=%d, n=%d, nfill=%d nsz=%d, tlen %d\r\n",
+ (long)desc->inet.port, desc->inet.s, n, nfill, nsz, tlen));
+
if (tlen > 0) {
if (tlen <= n) { /* got a packet */
*len = tlen;
@@ -9840,7 +10131,146 @@ static void tcp_inet_event(ErlDrvData e, ErlDrvEvent event)
return;
}
-#endif /* WIN32 */
+#elif defined(__OSE__) /* !__WIN32__ */
+/* The specific resolve signal function. It will return the socket descriptor
+ for which the select was issued */
+static ErlDrvOseEventId inet_resolve_signal(union SIGNAL *sig) {
+ DEBUGF(("%s(?#?): s=%d got signal %s, status = %d, extra = %d, sender = 0x%x\n",
+ __FUNCTION__,sig->async.fd,signo_to_string(sig->signo),
+ sig->async.event.status,
+ sig->async.event.extra,sender(&sig)));
+ if (sig->signo == SO_EVENT_READ_REPLY ||
+ sig->signo == SO_EVENT_ACCEPT_REPLY ||
+ sig->signo == SO_EVENT_EOF_REPLY ||
+ sig->signo == SO_EVENT_WRITE_REPLY ||
+ sig->signo == SO_EVENT_ERROR_REPLY ||
+ sig->signo == SO_EVENT_CONNECT_REPLY ) {
+ return sig->async.fd;
+ }
+
+ return -1;
+}
+
+static void inet_driver_select(inet_descriptor* desc,
+ int flags, int onoff) {
+ ASSERT(!desc->is_ignored);
+
+ if(onoff) {
+ desc->select_state |= flags;
+ } else {
+ desc->select_state &= ~flags;
+ }
+}
+
+static ssize_t writev_fallback(int fd, const struct iovec *iov, int iovcnt, int max_sz)
+{
+ size_t data_len = 0;
+ size_t sent = 0;
+ ssize_t n;
+ int i;
+
+ for(i = 0; i < iovcnt; i++)
+ {
+ data_len = iov[i].iov_len;
+tryagain:
+ n = sock_send(fd, iov[i].iov_base, data_len, 0);
+ if (IS_SOCKET_ERROR(n)) {
+ /* If buffer length is greater than the amount stack is able to
+ * send out then try to send at least max_sz (this comes with
+ * SO_EVENT_WRITE_REPLY signal*/
+ if ((errno == EMSGSIZE) && (max_sz > 0) && (data_len > max_sz)) {
+ data_len = max_sz;
+ goto tryagain;
+ }
+ break;
+ }
+ sent += n;
+ }
+ return sent;
+}
+
+#define OSE_EVENT_REQ(TCP_DESC,EVENT) do { \
+ union SIGNAL *sig = alloc(sizeof(struct OseAsyncSig), EVENT); \
+ sig->async.fd = INETP(TCP_DESC)->s; \
+ ose_request_event(INETP(TCP_DESC)->s, &sig, 1); \
+ DEBUGF(("%s(%ld): s=%d sent %s\r\n",__FUNCTION__, \
+ INETP(TCP_DESC)->port,INETP(TCP_DESC)->s,signo_to_string(EVENT))); \
+ } while(0)
+
+static void tcp_inet_ose_dispatch_signals(tcp_descriptor *desc,
+ int prev_select_state,
+ union SIGNAL *sig) {
+ if (sig) {
+ DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) {s=%d resend\r\n",
+ (long)INETP(desc)->port,INETP(desc)->s));
+ /* We are reacting to a signal, which means that if
+ the select_state for that signal is still activated
+ we should send a new signal */
+ switch (sig->signo) {
+ case SO_EVENT_READ_REPLY: {
+ if (INETP(desc)->select_state & FD_READ)
+ OSE_EVENT_REQ(desc,SO_EVENT_READ_REQUEST);
+ break;
+ }
+ case SO_EVENT_WRITE_REPLY: {
+ if (INETP(desc)->select_state & FD_WRITE)
+ OSE_EVENT_REQ(desc,SO_EVENT_WRITE_REQUEST);
+ break;
+ }
+ case SO_EVENT_CONNECT_REPLY: {
+ if (INETP(desc)->select_state & FD_CONNECT)
+ OSE_EVENT_REQ(desc,SO_EVENT_CONNECT_REQUEST);
+ break;
+ }
+ case SO_EVENT_ACCEPT_REPLY: {
+ if (INETP(desc)->select_state & FD_ACCEPT)
+ OSE_EVENT_REQ(desc,SO_EVENT_ACCEPT_REQUEST);
+ break;
+ }
+ case SO_EVENT_ERROR_REPLY: {
+ if (INETP(desc)->select_state & SOCK_FD_ERROR)
+ OSE_EVENT_REQ(desc,SO_EVENT_ERROR_REQUEST);
+ break;
+ }
+
+ }
+ DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) }\r\n",
+ (long)INETP(desc)->port));
+ }
+
+ if (INETP(desc)->select_state != prev_select_state) {
+ /* If the select state has changed we have to issue signals for
+ the state parts that have changed. */
+ int xor_select_state = INETP(desc)->select_state ^ prev_select_state;
+ DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) {s=%d select change\r\n",
+ (long)INETP(desc)->port,INETP(desc)->s));
+ if ((xor_select_state & FD_READ) &&
+ (INETP(desc)->select_state & FD_READ)) {
+ OSE_EVENT_REQ(desc,SO_EVENT_READ_REQUEST);
+ }
+ if ((xor_select_state & FD_WRITE) &&
+ (INETP(desc)->select_state & FD_WRITE)) {
+ OSE_EVENT_REQ(desc,SO_EVENT_WRITE_REQUEST);
+ }
+ if ((xor_select_state & FD_CONNECT) &&
+ (INETP(desc)->select_state & FD_CONNECT)) {
+ OSE_EVENT_REQ(desc,SO_EVENT_CONNECT_REQUEST);
+ }
+ if ((xor_select_state & FD_ACCEPT) &&
+ (INETP(desc)->select_state & FD_ACCEPT)) {
+ OSE_EVENT_REQ(desc,SO_EVENT_ACCEPT_REQUEST);
+ }
+ if ((xor_select_state & SOCK_FD_ERROR) &&
+ (INETP(desc)->select_state & SOCK_FD_ERROR)) {
+ OSE_EVENT_REQ(desc,SO_EVENT_ERROR_REQUEST);
+ }
+
+ DEBUGF(("tcp_inet_ose_dispatch_signals(%ld) }\r\n",
+ (long)INETP(desc)->port));
+ }
+}
+
+#endif /* __OSE__ */
/* socket has input:
@@ -9861,7 +10291,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event)
unsigned int len;
inet_address remote;
inet_async_op *this_op = desc->inet.opt;
-
+
len = sizeof(desc->inet.remote);
s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len);
if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) {
@@ -9930,7 +10360,6 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event)
while (desc->inet.state == INET_STATE_MULTI_ACCEPTING) {
len = sizeof(desc->inet.remote);
s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len);
-
if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) {
/* Just try again, no real error, keep the last return code */
goto done;
@@ -10256,6 +10685,49 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len)
return 0;
}
+#ifdef __OSE__
+
+static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event)
+{
+ union SIGNAL *event_sig = erl_drv_ose_get_signal(event);
+
+ while (event_sig) {
+ int prev_select_state = INETP((tcp_descriptor*)data)->select_state;
+ int res = tcp_inet_output((tcp_descriptor*)data, (HANDLE)event_sig);
+ if (res != -1) {
+ tcp_inet_ose_dispatch_signals((tcp_descriptor*)data,
+ prev_select_state,event_sig);
+ free_buf(&event_sig);
+ event_sig = erl_drv_ose_get_signal(event);
+ } else {
+ /* NOTE: here the event object could have been deallocated!!!!
+ inet_stop_select is called when doing driver_select(ERL_DRV_USE,0)
+ */
+ free_buf(&event_sig);
+ return;
+ }
+ }
+}
+
+static void tcp_inet_drv_input_ose(ErlDrvData data, ErlDrvEvent event)
+{
+ union SIGNAL *event_sig = erl_drv_ose_get_signal(event);
+
+ while (event_sig) {
+ int prev_select_state = INETP((tcp_descriptor*)data)->select_state;
+ int res = tcp_inet_input((tcp_descriptor*)data, (HANDLE)event);
+ if (res != -1) {
+ tcp_inet_ose_dispatch_signals((tcp_descriptor*)data, prev_select_state,
+ event_sig);
+ free_buf(&event_sig);
+ event_sig = erl_drv_ose_get_signal(event);
+ } else {
+ free_buf(&event_sig);
+ return;
+ }
+ }
+}
+#else
static void tcp_inet_drv_output(ErlDrvData data, ErlDrvEvent event)
{
(void)tcp_inet_output((tcp_descriptor*)data, (HANDLE)event);
@@ -10265,6 +10737,7 @@ static void tcp_inet_drv_input(ErlDrvData data, ErlDrvEvent event)
{
(void)tcp_inet_input((tcp_descriptor*)data, (HANDLE)event);
}
+#endif
/* socket ready for ouput:
** 1. INET_STATE_CONNECTING => non block connect ?
@@ -10330,6 +10803,13 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
ssize_t n;
SysIOVec* iov;
+#ifdef __OSE__
+ /* For large size buffers case the amount of data that the stack is
+ able to send out (received in the .extra field) should be passed
+ down to writev_fallback */
+ n = event ? ((union SIGNAL*)event)->async.event.extra : 0;
+#endif
+
if ((iov = driver_peekq(ix, &vsize)) == NULL) {
sock_select(INETP(desc), FD_WRITE, 0);
send_empty_out_q_msgs(INETP(desc));
@@ -10341,8 +10821,8 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
if (IS_SOCKET_ERROR(sock_sendv(desc->inet.s, iov, vsize, &n, 0))) {
write_error:
if ((sock_errno() != ERRNO_BLOCK) && (sock_errno() != EINTR)) {
- DEBUGF(("tcp_inet_output(%ld): sock_sendv(%d) errno = %d\r\n",
- (long)desc->inet.port, vsize, sock_errno()));
+ DEBUGF(("tcp_inet_output(%ld): sock_sendv(%d) errno = %d (errno %d)\r\n",
+ (long)desc->inet.port, vsize, sock_errno(), errno));
ret = tcp_send_error(desc, sock_errno());
goto done;
}
@@ -10355,6 +10835,12 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
sizes > (max 32 bit signed int) */
size_t howmuch = 0x7FFFFFFF; /* max signed 32 bit */
int x;
+#ifdef __OSE__
+ /* For EWOULDBLOCK sock_sendv returns 0 so we have to be sure it
+ wasn't the case */
+ if(sock_errno() == ERRNO_BLOCK)
+ goto done;
+#endif
for(x = 0; x < vsize && iov[x].iov_len == 0; ++x)
;
if (x < vsize) {
@@ -10499,6 +10985,7 @@ static udp_descriptor* sctp_inet_copy(udp_descriptor* desc, SOCKET s, int* err)
+#ifdef HAVE_UDP
static int packet_inet_init()
{
return 0;
@@ -10529,6 +11016,7 @@ static ErlDrvData udp_inet_start(ErlDrvPort port, char *args)
set_default_msgq_limits(port);
return data;
}
+#endif
#ifdef HAVE_SCTP
static ErlDrvData sctp_inet_start(ErlDrvPort port, char *args)
@@ -10539,6 +11027,7 @@ static ErlDrvData sctp_inet_start(ErlDrvPort port, char *args)
}
#endif
+#ifdef HAVE_UDP
static void packet_inet_stop(ErlDrvData e)
{
/* There should *never* be any "empty out q" subscribers on
@@ -11049,7 +11538,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
else
inet_reply_ok(desc);
}
-
+#endif
#ifdef __WIN32__
static void packet_inet_event(ErlDrvData e, ErlDrvEvent event)
@@ -11071,6 +11560,7 @@ static void packet_inet_event(ErlDrvData e, ErlDrvEvent event)
#endif
+#ifdef HAVE_UDP
static void packet_inet_drv_input(ErlDrvData e, ErlDrvEvent event)
{
(void) packet_inet_input((udp_descriptor*)e, (HANDLE)event);
@@ -11327,6 +11817,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event)
DEBUGF(("packet_inet_output(%ld) }\r\n", (long)desc->port));
return ret;
}
+#endif
/*---------------------------------------------------------------------------*/
diff --git a/erts/emulator/drivers/common/zlib_drv.c b/erts/emulator/drivers/common/zlib_drv.c
index 3fe5d282dc..3143e4511d 100644
--- a/erts/emulator/drivers/common/zlib_drv.c
+++ b/erts/emulator/drivers/common/zlib_drv.c
@@ -21,6 +21,9 @@
* ZLib interface for erlang
*
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
#include <stdio.h>
#include <zlib.h>
#include <errno.h>
diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c
index 42f41c5f3d..878beb055b 100644
--- a/erts/emulator/drivers/unix/unix_efile.c
+++ b/erts/emulator/drivers/unix/unix_efile.c
@@ -360,7 +360,12 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
int fd;
int mode; /* Open mode. */
- if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) {
+ 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.
diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c
index 480ba23239..a321bb9641 100644
--- a/erts/emulator/drivers/win32/win_efile.c
+++ b/erts/emulator/drivers/win32/win_efile.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -29,12 +29,27 @@
#include <wchar.h>
#include "erl_efile.h"
+#define DBG_TRACE_MASK 0
+/* 1 = file name ops
+ * 2 = file descr ops
+ * 4 = errors
+ * 8 = path name conversion
+ */
+#if !DBG_TRACE_MASK
+# define DBG_TRACE(M,S)
+# define DBG_TRACE1(M,FMT,A)
+# define DBG_TRACE2(M,FMT,A,B)
+#else
+# define DBG_TRACE(M,S) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: %s\r\n", __LINE__, (WCHAR*)(S)); }while(0)
+# define DBG_TRACE1(M,FMT,A) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: " L##FMT L"\r\n", __LINE__, (A)); }while(0)
+# define DBG_TRACE2(M,FMT,A,B) do { if ((M)&DBG_TRACE_MASK) fwprintf(stderr, L"DBG_TRACE %d: " L##FMT L"\r\n", __LINE__, (A), (B)); }while(0)
+#endif
+
/*
* Microsoft-specific function to map a WIN32 error code to a Posix errno.
*/
#define ISSLASH(a) ((a) == L'\\' || (a) == L'/')
-
#define ISDIR(st) (((st).st_mode&S_IFMT) == S_IFDIR)
#define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG)
@@ -69,10 +84,92 @@
static int check_error(int result, Efile_error* errInfo);
static int set_error(Efile_error* errInfo);
+static int set_os_errno(Efile_error* errInfo, DWORD os_errno);
static int is_root_unc_name(const WCHAR *path);
static int extract_root(WCHAR *name);
static unsigned short dos_to_posix_mode(int attr, const WCHAR *name);
+
+struct wpath_tmp_buffer {
+ struct wpath_tmp_buffer* next;
+ WCHAR buffer[1];
+};
+
+typedef struct {
+ Efile_error* errInfo;
+ struct wpath_tmp_buffer* buf_list;
+}Efile_call_state;
+
+static void call_state_init(Efile_call_state* state, Efile_error* errInfo)
+{
+ state->errInfo = errInfo;
+ state->buf_list = NULL;
+}
+static WCHAR* wpath_tmp_alloc(Efile_call_state* state, size_t len)
+{
+ size_t sz = offsetof(struct wpath_tmp_buffer, buffer)
+ + (len+1)*sizeof(WCHAR);
+ struct wpath_tmp_buffer* p = driver_alloc(sz);
+ p->next = state->buf_list;
+ state->buf_list = p;
+ return p->buffer;
+}
+static void call_state_free(Efile_call_state* state)
+{
+ while(state->buf_list) {
+ struct wpath_tmp_buffer* next = state->buf_list->next;
+ driver_free(state->buf_list);
+ state->buf_list = next;
+ }
+}
+static WCHAR* get_cwd_wpath_tmp(Efile_call_state* state)
+{
+ WCHAR dummy;
+ DWORD size = GetCurrentDirectoryW(0, &dummy);
+ WCHAR* ret = NULL;
+
+ if (size) {
+ ret = wpath_tmp_alloc(state, size);
+ if (!GetCurrentDirectoryW(size, ret)) {
+ ret = NULL;
+ }
+ }
+ return ret;
+}
+static WCHAR* get_full_wpath_tmp(Efile_call_state* state,
+ const WCHAR* file,
+ WCHAR** file_part,
+ DWORD extra)
+{
+ WCHAR dummy;
+ DWORD size = GetFullPathNameW(file, 0, &dummy, NULL);
+ WCHAR* ret = NULL;
+
+ if (size) {
+ int ok;
+ ret = wpath_tmp_alloc(state, size + extra);
+ if (file_part) {
+ ok = (GetFullPathNameW(file, size, ret, file_part) != 0);
+ }
+ else {
+ ok = (_wfullpath(ret, file, size) != NULL);
+ }
+ if (!ok) {
+ ret = NULL;
+ }
+ }
+ return ret;
+}
+
+static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max);
+static int do_rmdir(Efile_call_state*, char* name);
+static int do_rename(Efile_call_state*, char* src, char* dst);
+static int do_readdir(Efile_call_state*, char* name, EFILE_DIR_HANDLE*, char* buffer, size_t *size);
+static int do_fileinfo(Efile_call_state*, Efile_info*, char* orig_name, int info_for_link);
+static char* do_readlink(Efile_call_state*, char* name, char* buffer, size_t size);
+static int do_altname(Efile_call_state*, char* orig_name, char* buffer, size_t size);
+
+
static int errno_map(DWORD last_error) {
switch (last_error) {
@@ -154,6 +251,8 @@ static int errno_map(DWORD last_error) {
return EAGAIN;
case ERROR_CANT_RESOLVE_FILENAME:
return EMLINK;
+ case ERROR_PRIVILEGE_NOT_HELD:
+ return EPERM;
case ERROR_ARENA_TRASHED:
case ERROR_INVALID_BLOCK:
case ERROR_BAD_ENVIRONMENT:
@@ -176,11 +275,23 @@ check_error(int result, Efile_error* errInfo)
if (result < 0) {
errInfo->posix_errno = errno;
errInfo->os_errno = GetLastError();
+ DBG_TRACE2(4, "ERROR os_error=%d errno=%d @@@@@@@@@@@@@@@@@@@@@@@@@@@@",
+ errInfo->os_errno, errInfo->posix_errno);
return 0;
}
return 1;
}
+static void
+save_last_error(Efile_error* errInfo)
+{
+ errInfo->posix_errno = errno;
+ errInfo->os_errno = GetLastError();
+ DBG_TRACE2(4, "ERROR os_error=%d errno=%d $$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
+ errInfo->os_errno, errInfo->posix_errno);
+}
+
+
/*
* Fills the provided error information structure with information
* with the error code given by GetLastError() and its corresponding
@@ -192,7 +303,18 @@ check_error(int result, Efile_error* errInfo)
static int
set_error(Efile_error* errInfo)
{
- errInfo->posix_errno = errno_map(errInfo->os_errno = GetLastError());
+ set_os_errno(errInfo, GetLastError());
+ return 0;
+}
+
+
+static int
+set_os_errno(Efile_error* errInfo, DWORD os_errno)
+{
+ errInfo->os_errno = os_errno;
+ errInfo->posix_errno = errno_map(os_errno);
+ DBG_TRACE2(4, "ERROR os_error=%d errno=%d ############################",
+ errInfo->os_errno, errInfo->posix_errno);
return 0;
}
@@ -226,21 +348,151 @@ win_writev(Efile_error* errInfo,
}
+/* Check '*pathp' and convert it if needed to something that windows will accept.
+ * Typically use UNC path with \\?\ prefix if absolute path is longer than 260.
+ */
+static void ensure_wpath(Efile_call_state* state, WCHAR** pathp)
+{
+ ensure_wpath_max(state, pathp, MAX_PATH);
+}
+
+static void ensure_wpath_max(Efile_call_state* state, WCHAR** pathp, size_t max)
+{
+ WCHAR* path = *pathp;
+ WCHAR* p;
+ size_t len = wcslen(path);
+ int unc_fixup = 0;
+
+ if (path[0] == 0) {
+ DBG_TRACE(8, L"Let empty path pass through");
+ return;
+ }
+
+ DBG_TRACE1(8,"IN: %s", path);
+
+ if (path[1] == L':' && ISSLASH(path[2])) { /* absolute path */
+ if (len >= max) {
+ WCHAR *src, *dst;
+
+ *pathp = wpath_tmp_alloc(state, 4+len+1);
+ dst = *pathp;
+ wcscpy(dst, L"\\\\?\\");
+ for (src=path,dst+=4; *src; src++) {
+ if (*src == L'/') {
+ if (dst[-1] != L'\\') {
+ *dst++ = L'\\';
+ }
+ /*else ignore redundant slashes */
+ }
+ else
+ *dst++ = *src;
+ }
+ *dst = 0;
+ unc_fixup = 1;
+ }
+ }
+ else if (!(ISSLASH(path[0]) && ISSLASH(path[1]))) { /* relative path */
+ DWORD cwdLen = GetCurrentDirectoryW(0, NULL);
+ DWORD absLen = cwdLen + 1 + len;
+ if (absLen >= max) {
+ WCHAR *fullPath = wpath_tmp_alloc(state, 4+4+absLen);
+ DWORD fullLen;
+
+ fullLen = GetFullPathNameW(path, 4 + absLen, fullPath+4, NULL);
+ if (fullLen >= 4+absLen) {
+ *pathp = path;
+ DBG_TRACE2(8,"ensure_wpath FAILED absLen=%u %s", (int)absLen, path);
+ return;
+ }
+ /* GetFullPathNameW can return paths longer than MAX_PATH without the \\?\ prefix.
+ * At least seen on Windows 7. Go figure...
+ */
+ if (fullLen >= max && wcsncmp(fullPath+4, L"\\\\?\\", 4) != 0) {
+ wcsncpy(fullPath, L"\\\\?\\", 4);
+ *pathp = fullPath;
+ }
+ else {
+ *pathp = fullPath + 4;
+ }
+ }
+ }
+
+ if (unc_fixup) {
+ WCHAR* endp;
+
+ p = *pathp;
+ len = wcslen(p);
+ endp = p + len;
+ if (len > 4) {
+ p += 4;
+ while (*p) {
+ if (p[0] == L'\\' && p[1] == L'.') {
+ if (p[2] == L'\\' || !p[2]) { /* single dot */
+ wmemmove(p, p+2, (&endp[1] - &p[2]));
+ endp -= 2;
+ }
+ else if (p[2] == L'.' && (p[3] == L'\\' || !p[3])) { /* double dot */
+ WCHAR* r;
+ for (r=p-1; *r == L'\\'; --r)
+ /*skip redundant slashes*/;
+ for (; *r != L'\\'; --r)
+ /*find start of prev directory*/;
+ if (r < *pathp + 6)
+ break;
+ wmemmove(r, p+3, (&endp[1] - &p[3]));
+ p = r;
+ }
+ else p += 3;
+ }
+ else ++p;
+ }
+ }
+ }
+ DBG_TRACE1(8,"OUT: %s", *pathp);
+}
int
efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of directory to create. */
{
- return check_error(_wmkdir((WCHAR *) name), errInfo);
+ Efile_call_state state;
+ WCHAR* wname = (WCHAR*)name;
+ int ret;
+
+ DBG_TRACE(1, name);
+ call_state_init(&state, errInfo);
+ ensure_wpath_max(&state, &wname, 248); /* Yes, 248 limit for normal paths */
+
+ ret = (int) CreateDirectoryW(wname, NULL);
+ if (!ret)
+ set_error(errInfo);
+
+ call_state_free(&state);
+ return ret;
}
int
efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of directory to delete. */
{
+ Efile_call_state state;
+ int ret;
+
+ DBG_TRACE(1, name);
+ call_state_init(&state, errInfo);
+ ret = do_rmdir(&state, name);
+ call_state_free(&state);
+ return ret;
+}
+
+static int do_rmdir(Efile_call_state* state, char* name)
+{
OSVERSIONINFO os;
DWORD attr;
WCHAR *wname = (WCHAR *) name;
+ WCHAR *buffer = NULL;
+
+ ensure_wpath(state, &wname);
if (RemoveDirectoryW(wname) != FALSE) {
return 1;
@@ -270,10 +522,9 @@ efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
if (os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
HANDLE handle;
WIN32_FIND_DATAW data;
- WCHAR buffer[2*MAX_PATH];
- int len;
+ int len = wcslen(wname);
- len = wcslen(wname);
+ buffer = wpath_tmp_alloc(state, len + 4);
wcscpy(buffer, wname);
if (buffer[0] && buffer[len-1] != L'\\' && buffer[len-1] != L'/') {
wcscat(buffer, L"\\");
@@ -311,16 +562,30 @@ efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
}
end:
- return check_error(-1, errInfo);
+ save_last_error(state->errInfo);
+ return 0;
}
int
efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of file to delete. */
{
+ Efile_call_state state;
+ int ret;
+ DBG_TRACE(1, name);
+ call_state_init(&state, errInfo);
+ ret = do_delete_file(&state, name);
+ call_state_free(&state);
+ return ret;
+}
+
+static int do_delete_file(Efile_call_state* state, char* name)
+{
DWORD attr;
WCHAR *wname = (WCHAR *) name;
+ ensure_wpath(state, &wname);
+
if (DeleteFileW(wname) != FALSE) {
return 1;
}
@@ -359,7 +624,7 @@ efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */
errno = EACCES;
}
- return check_error(-1, errInfo);
+ return check_error(-1, state->errInfo);
}
/*
@@ -393,14 +658,29 @@ efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */
*/
int
-efile_rename(Efile_error* errInfo, /* Where to return error codes. */
- char* src, /* Original name. */
- char* dst) /* New name. */
+efile_rename(Efile_error* errInfo, char* src, char* dst)
+{
+ Efile_call_state state;
+ int ret;
+ DBG_TRACE(1, src);
+ call_state_init(&state, errInfo);
+ ret = do_rename(&state, src, dst);
+ call_state_free(&state);
+ return ret;
+}
+
+static int
+do_rename(Efile_call_state* state,
+ char* src, /* Original name. */
+ char* dst) /* New name. */
{
DWORD srcAttr, dstAttr;
WCHAR *wsrc = (WCHAR *) src;
WCHAR *wdst = (WCHAR *) dst;
-
+
+ ensure_wpath(state, &wsrc);
+ ensure_wpath(state, &wdst);
+
if (MoveFileW(wsrc, wdst) != FALSE) {
return 1;
}
@@ -417,23 +697,27 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */
if (errno == EBADF) {
errno = EACCES;
- return check_error(-1, errInfo);
+ return check_error(-1, state->errInfo);
}
if (errno == EACCES) {
decode:
if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) {
- WCHAR srcPath[MAX_PATH], dstPath[MAX_PATH];
+ WCHAR *srcPath, *dstPath;
WCHAR *srcRest, *dstRest;
int size;
- size = GetFullPathNameW(wsrc, MAX_PATH, srcPath, &srcRest);
- if ((size == 0) || (size > MAX_PATH)) {
- return check_error(-1, errInfo);
+ srcPath = get_full_wpath_tmp(state, wsrc, &srcRest, 0);
+ if (!srcPath) {
+ save_last_error(state->errInfo);
+ return 0;
}
- size = GetFullPathNameW(wdst, MAX_PATH, dstPath, &dstRest);
- if ((size == 0) || (size > MAX_PATH)) {
- return check_error(-1, errInfo);
+
+ dstPath = get_full_wpath_tmp(state, wdst, &dstRest, 0);
+ if (!dstPath) {
+ save_last_error(state->errInfo);
+ return 0;
}
+
if (srcRest == NULL) {
srcRest = srcPath + wcslen(srcPath);
}
@@ -538,14 +822,16 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */
* put temp file back to old name.
*/
- WCHAR tempName[MAX_PATH];
- int result, size;
+ WCHAR *tempName;
+ int result;
WCHAR *rest;
- size = GetFullPathNameW(wdst, MAX_PATH, tempName, &rest);
- if ((size == 0) || (size > MAX_PATH) || (rest == NULL)) {
- return check_error(-1, errInfo);
+ tempName = get_full_wpath_tmp(state, wdst, &rest, 14);
+ if (!tempName || !rest) {
+ save_last_error(state->errInfo);
+ return 0;
}
+
*rest = L'\0';
result = -1;
if (GetTempFileNameW(tempName, L"erlr", 0, tempName) != 0) {
@@ -578,7 +864,6 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */
/*
* Decode the EACCES to a more meaningful error.
*/
-
goto decode;
}
}
@@ -586,16 +871,20 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */
}
}
}
- return check_error(-1, errInfo);
+ return check_error(-1, state->errInfo);
}
int
efile_chdir(Efile_error* errInfo, /* Where to return error codes. */
char* name) /* Name of directory to make current. */
-{
- int success = check_error(_wchdir((WCHAR *) name), errInfo);
- if (!success && errInfo->posix_errno == EINVAL)
- /* POSIXification of errno */
+{
+ /* We don't even try to handle long paths here
+ * as current working directory is always limited to MAX_PATH
+ * even if we use UNC paths and SetCurrentDirectoryW()
+ */
+ int success = check_error(_wchdir((WCHAR *) name), errInfo);
+ if (!success && errInfo->posix_errno == EINVAL)
+ /* POSIXification of errno */
errInfo->posix_errno = ENOENT;
return success;
}
@@ -608,28 +897,45 @@ efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */
{
WCHAR *wbuffer = (WCHAR *) buffer;
size_t wbuffer_size = size / 2;
- if (_wgetdcwd(drive, wbuffer, wbuffer_size) == NULL)
+ DBG_TRACE(1, L"#getdcwd#");
+ if (_wgetdcwd(drive, wbuffer, wbuffer_size) == NULL) {
return check_error(-1, errInfo);
+ }
+ DBG_TRACE1(8, "getdcwd OS=%s", wbuffer);
+ if (wcsncmp(wbuffer, L"\\\\?\\", 4) == 0) {
+ wmemmove(wbuffer, wbuffer+4, wcslen(wbuffer+4)+1);
+ }
for ( ; *wbuffer; wbuffer++)
if (*wbuffer == L'\\')
*wbuffer = L'/';
+ DBG_TRACE1(8, "getdcwd ERLANG=%s", (WCHAR*)buffer);
return 1;
}
int
-efile_readdir(Efile_error* errInfo, /* Where to return error codes. */
- char* name, /* Name of directory to list */
- EFILE_DIR_HANDLE* dir_handle, /* Handle of opened directory or NULL */
- char* buffer, /* Buffer to put one filename in */
- size_t *size) /* in-out size of buffer/size of filename excluding zero
- termination in bytes*/
+efile_readdir(Efile_error* errInfo, char* name, EFILE_DIR_HANDLE* dir_handle,
+ char* buffer, size_t *size)
+{
+ Efile_call_state state;
+ int ret;
+ DBG_TRACE(dir_handle?2:1, name);
+ call_state_init(&state, errInfo);
+ ret = do_readdir(&state, name, dir_handle, buffer, size);
+ call_state_free(&state);
+ return ret;
+}
+
+static int do_readdir(Efile_call_state* state,
+ char* name, /* Name of directory to list */
+ EFILE_DIR_HANDLE* dir_handle, /* Handle of opened directory or NULL */
+ char* buffer, /* Buffer to put one filename in */
+ size_t *size) /* in-out size of buffer/size of filename excluding zero
+ termination in bytes*/
{
HANDLE dir; /* Handle to directory. */
- WCHAR wildcard[MAX_PATH]; /* Wildcard to search for. */
WIN32_FIND_DATAW findData; /* Data found by FindFirstFile() or FindNext(). */
/* Alignment is not honored, this works on x86 because of alignment fixup by processor.
Not perfect, but faster than alinging by hand (really) */
- WCHAR *wname = (WCHAR *) name;
WCHAR *wbuffer = (WCHAR *) buffer;
/*
@@ -637,13 +943,15 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */
*/
if (*dir_handle == NULL) {
- int length = wcslen(wname);
+ WCHAR *wname = (WCHAR *) name;
+ WCHAR* wildcard;
+ int length;
WCHAR* s;
- if (length+3 >= MAX_PATH) {
- errno = ENAMETOOLONG;
- return check_error(-1, errInfo);
- }
+ ensure_wpath_max(state, &wname, MAX_PATH-2);
+ length = wcslen(wname);
+
+ wildcard = wpath_tmp_alloc(state, length+3);
wcscpy(wildcard, wname);
s = wildcard+length-1;
@@ -653,8 +961,10 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */
*++s = L'\0';
DEBUGF(("Reading %ws\n", wildcard));
dir = FindFirstFileW(wildcard, &findData);
- if (dir == INVALID_HANDLE_VALUE)
- return set_error(errInfo);
+ if (dir == INVALID_HANDLE_VALUE) {
+ set_error(state->errInfo);
+ return 0;
+ }
*dir_handle = (EFILE_DIR_HANDLE) dir;
if (!IS_DOT_OR_DOTDOT(findData.cFileName)) {
@@ -664,7 +974,6 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */
}
}
-
/*
* Retrieve the name of the next file using the directory handle.
*/
@@ -681,24 +990,36 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */
}
if (GetLastError() == ERROR_NO_MORE_FILES) {
- FindClose(dir);
- errInfo->posix_errno = errInfo->os_errno = 0;
- return 0;
+ state->errInfo->posix_errno = state->errInfo->os_errno = 0;
+ }
+ else {
+ set_error(state->errInfo);
}
-
- set_error(errInfo);
FindClose(dir);
return 0;
}
}
int
-efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
- char* name, /* Name of directory to open. */
- int flags, /* Flags to use for opening. */
- int* pfd, /* Where to store the file descriptor. */
- Sint64* pSize) /* Where to store the size of the file. */
+efile_openfile(Efile_error* errInfo, char* name, int flags, int* pfd, Sint64* pSize)
{
+ Efile_call_state state;
+ int ret;
+ DBG_TRACE1(1, "openfile(%s)", name);
+ call_state_init(&state, errInfo);
+ ret = do_openfile(&state, name, flags, pfd, pSize);
+ call_state_free(&state);
+ return ret;
+}
+
+static
+int do_openfile(Efile_call_state* state, /* Where to return error codes. */
+ char* name, /* Name of directory to open. */
+ int flags, /* Flags to use for opening. */
+ int* pfd, /* Where to store the file descriptor. */
+ Sint64* pSize) /* Where to store the size of the file. */
+{
+ Efile_error* errInfo = state->errInfo;
BY_HANDLE_FILE_INFORMATION fileInfo; /* File information from a handle. */
HANDLE fd; /* Handle to open file. */
DWORD access; /* Access mode: GENERIC_READ, GENERIC_WRITE. */
@@ -735,6 +1056,7 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
if (flags & EFILE_MODE_EXCL) {
crFlags = CREATE_NEW;
}
+ ensure_wpath(state, &wname);
fd = CreateFileW(wname, access,
FILE_SHARE_FLAGS,
NULL, crFlags, flagsAndAttrs, NULL);
@@ -777,34 +1099,56 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
}
int
-efile_may_openfile(Efile_error* errInfo, char *name) {
+efile_may_openfile(Efile_error* errInfo, char *name)
+{
+ Efile_call_state state;
WCHAR *wname = (WCHAR *) name;
DWORD attr;
+ int ret;
+ DBG_TRACE(1, name);
+ call_state_init(&state, errInfo);
+ ensure_wpath(&state, &wname);
if ((attr = GetFileAttributesW(wname)) == INVALID_FILE_ATTRIBUTES) {
errno = ENOENT;
- return check_error(-1, errInfo);
+ ret = check_error(-1, errInfo);
}
-
- if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+ else if (attr & FILE_ATTRIBUTE_DIRECTORY) {
errno = EISDIR;
- return check_error(-1, errInfo);
+ ret = check_error(-1, errInfo);
}
- return 1;
+ else ret = 1;
+
+ call_state_free(&state);
+ return ret;
}
void
efile_closefile(fd)
int fd; /* File descriptor for file to close. */
{
+ DBG_TRACE(2, L"");
CloseHandle((HANDLE) fd);
}
+FILE* efile_wfopen(const WCHAR* name, const WCHAR* mode)
+{
+ Efile_call_state state;
+ Efile_error dummy;
+ FILE* f;
+ call_state_init(&state, &dummy);
+ ensure_wpath(&state, (WCHAR**)&name);
+ f = _wfopen(name, mode);
+ call_state_free(&state);
+ return f;
+}
+
int
efile_fdatasync(errInfo, fd)
Efile_error* errInfo; /* Where to return error codes. */
int fd; /* File descriptor for file to sync. */
{
+ DBG_TRACE(2, L"");
/* Not available in Windows, just call regular fsync */
return efile_fsync(errInfo, fd);
}
@@ -814,6 +1158,7 @@ efile_fsync(errInfo, fd)
Efile_error* errInfo; /* Where to return error codes. */
int fd; /* File descriptor for file to sync. */
{
+ DBG_TRACE(2, L"");
if (!FlushFileBuffers((HANDLE) fd)) {
return check_error(-1, errInfo);
}
@@ -824,64 +1169,87 @@ int
efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
char* orig_name, int info_for_link)
{
+ Efile_call_state state;
+ int ret;
+ DBG_TRACE(1, L"");
+ call_state_init(&state, errInfo);
+ ret = do_fileinfo(&state, pInfo, orig_name, info_for_link);
+ call_state_free(&state);
+ return ret;
+}
+
+static int
+do_fileinfo(Efile_call_state* state, Efile_info* pInfo,
+ char* orig_name, int info_for_link)
+{
+ Efile_error* errInfo = state->errInfo;
HANDLE findhandle; /* Handle returned by FindFirstFile(). */
WIN32_FIND_DATAW findbuf; /* Data return by FindFirstFile(). */
- WCHAR name[_MAX_PATH];
+ WCHAR* name = NULL;
+ WCHAR* win_path;
int name_len;
- WCHAR *path;
- WCHAR pathbuf[_MAX_PATH];
int drive; /* Drive for filename (1 = A:, 2 = B: etc). */
- WCHAR *worig_name = (WCHAR *) orig_name;
+ WCHAR *worig_name = (WCHAR *) orig_name;
+ ensure_wpath(state, &worig_name);
/* Don't allow wildcards to be interpreted by system */
- if (wcspbrk(worig_name, L"?*")) {
- enoent:
- errInfo->posix_errno = ENOENT;
- errInfo->os_errno = ERROR_FILE_NOT_FOUND;
- return 0;
- }
/*
* Move the name to a buffer and make sure to remove a trailing
* slash, because it causes FindFirstFile() to fail on Win95.
*/
- if ((name_len = wcslen(worig_name)) >= _MAX_PATH) {
- goto enoent;
- } else {
- wcscpy(name, worig_name);
- if (name_len > 2 && ISSLASH(name[name_len-1]) &&
- name[name_len-2] != L':') {
- name[name_len-1] = L'\0';
- }
+ name_len = wcslen(worig_name);
+
+ name = wpath_tmp_alloc(state, name_len+1);
+ wcscpy(name, worig_name);
+ if (name_len > 2 && ISSLASH(name[name_len-1]) &&
+ name[name_len-2] != L':') {
+ name[name_len-1] = L'\0';
}
-
+
+ win_path = name;
+ if (wcsncmp(name, L"\\\\?\\", 4) == 0) {
+ win_path += 4;
+ }
+
+ if (wcspbrk(win_path, L"?*")) {
+ enoent:
+ errInfo->posix_errno = ENOENT;
+ errInfo->os_errno = ERROR_FILE_NOT_FOUND;
+ return 0;
+ }
+
/* Try to get disk from name. If none, get current disk. */
- if (name[1] != L':') {
+ if (win_path[1] != L':') {
+ WCHAR* cwd_path = get_cwd_wpath_tmp(state);
drive = 0;
- if (GetCurrentDirectoryW(_MAX_PATH, pathbuf) &&
- pathbuf[1] == L':') {
- drive = towlower(pathbuf[0]) - L'a' + 1;
+ if (cwd_path[1] == L':') {
+ drive = towlower(cwd_path[0]) - L'a' + 1;
}
- } else if (*name && name[2] == L'\0') {
+ } else if (*win_path && win_path[2] == L'\0') {
/*
* X: and nothing more is an error.
*/
errInfo->posix_errno = ENOENT;
errInfo->os_errno = ERROR_FILE_NOT_FOUND;
return 0;
- } else
- drive = towlower(*name) - L'a' + 1;
+ } else {
+ drive = towlower(*win_path) - L'a' + 1;
+ }
findhandle = FindFirstFileW(name, &findbuf);
if (findhandle == INVALID_HANDLE_VALUE) {
+ WCHAR* path = NULL;
+
if (!(wcspbrk(name, L"./\\") &&
- (path = _wfullpath(pathbuf, name, _MAX_PATH)) &&
+ (path = get_full_wpath_tmp(state, name, NULL, 0)) &&
/* root dir. ('C:\') or UNC root dir. ('\\server\share\') */
((wcslen(path) == 3) || is_root_unc_name(path)) &&
(GetDriveTypeW(path) > 1) ) ) {
+
errInfo->posix_errno = ENOENT;
errInfo->os_errno = ERROR_FILE_NOT_FOUND;
return 0;
@@ -908,13 +1276,11 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
/*
* given that we know this is a symlink,
we should be able to find its target */
- WCHAR target_name[_MAX_PATH];
- if (efile_readlink(errInfo, (char *) name,
- (char *) target_name,
- _MAX_PATH * sizeof(WCHAR)) == 1) {
+ WCHAR* target_name = (WCHAR*) do_readlink(state, (char *) name, NULL, 0);
+ if (target_name) {
FindClose(findhandle);
- return efile_fileinfo(errInfo, pInfo,
- (char *) target_name, info_for_link);
+ return do_fileinfo(state, pInfo,
+ (char *) target_name, info_for_link);
}
}
@@ -981,6 +1347,20 @@ efile_write_info(Efile_error* errInfo,
Efile_info* pInfo,
char* name)
{
+ Efile_call_state state;
+ int ret;
+ call_state_init(&state, errInfo);
+ ret = do_write_info(&state, pInfo, name);
+ call_state_free(&state);
+ return ret;
+}
+
+static int
+do_write_info(Efile_call_state* state,
+ Efile_info* pInfo,
+ char* name)
+{
+ Efile_error* errInfo = state->errInfo;
SYSTEMTIME timebuf;
FILETIME ModifyFileTime;
FILETIME AccessFileTime;
@@ -990,6 +1370,10 @@ efile_write_info(Efile_error* errInfo,
DWORD tempAttr;
WCHAR *wname = (WCHAR *) name;
+ DBG_TRACE(1, name);
+
+ ensure_wpath(state, &wname);
+
/*
* Get the attributes for the file.
*/
@@ -1066,7 +1450,9 @@ char* buf; /* Buffer to write. */
size_t count; /* Number of bytes to write. */
Sint64 offset; /* where to write it */
{
- int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
+ int res;
+ DBG_TRACE(2, L"");
+ res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
if (res) {
return efile_write(errInfo, EFILE_MODE_WRITE, fd, buf, count);
} else {
@@ -1084,7 +1470,9 @@ char* buf; /* Buffer to read into. */
size_t count; /* Number of bytes to read. */
size_t* pBytesRead; /* Where to return number of bytes read. */
{
- int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
+ int res;
+ DBG_TRACE(2, L"");
+ res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
if (res) {
return efile_read(errInfo, EFILE_MODE_READ, fd, buf, count, pBytesRead);
} else {
@@ -1106,6 +1494,7 @@ size_t count; /* Number of bytes to write. */
OVERLAPPED overlapped;
OVERLAPPED* pOverlapped = NULL;
+ DBG_TRACE(2, L"");
if (flags & EFILE_MODE_APPEND) {
memset(&overlapped, 0, sizeof(overlapped));
overlapped.Offset = 0xffffffff;
@@ -1135,6 +1524,7 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */
OVERLAPPED overlapped;
OVERLAPPED* pOverlapped = NULL;
+ DBG_TRACE(2, L"");
ASSERT(iovcnt >= 0);
if (flags & EFILE_MODE_APPEND) {
@@ -1171,6 +1561,8 @@ size_t count; /* Number of bytes to read. */
size_t* pBytesRead; /* Where to return number of bytes read. */
{
DWORD nbytes = 0;
+
+ DBG_TRACE(2, L"");
if (!ReadFile((HANDLE) fd, buf, count, &nbytes, NULL))
return set_error(errInfo);
@@ -1190,6 +1582,7 @@ Sint64* new_location; /* Resulting new location in file. */
{
LARGE_INTEGER off, new_loc;
+ DBG_TRACE(2, L"");
switch (origin) {
case EFILE_SEEK_SET: origin = FILE_BEGIN; break;
case EFILE_SEEK_CUR: origin = FILE_CURRENT; break;
@@ -1221,6 +1614,7 @@ Efile_error* errInfo; /* Where to return error codes. */
int *fd; /* File descriptor for file to truncate. */
int flags;
{
+ DBG_TRACE(2, L"");
if (!SetEndOfFile((HANDLE) (*fd)))
return set_error(errInfo);
return 1;
@@ -1373,9 +1767,24 @@ dos_to_posix_mode(int attr, const WCHAR *name)
return uxmode;
}
+
int
efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
{
+ Efile_call_state state;
+ int ret;
+ DBG_TRACE(1, name);
+ call_state_init(&state, errInfo);
+ ret = !!do_readlink(&state, name, buffer, size);
+ call_state_free(&state);
+ return ret;
+}
+
+/* If buffer==0, return buffer allocated by wpath_tmp_allocate
+*/
+static char*
+do_readlink(Efile_call_state* state, char* name, char* buffer, size_t size)
+{
/*
* load dll and see if we have CreateSymbolicLink at runtime:
* (Vista only)
@@ -1383,6 +1792,9 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
HINSTANCE hModule = NULL;
WCHAR *wname = (WCHAR *) name;
WCHAR *wbuffer = (WCHAR *) buffer;
+ DWORD wsize = size / sizeof(WCHAR);
+ char* ret = NULL;
+
if ((hModule = LoadLibrary("kernel32.dll")) != NULL) {
typedef DWORD (WINAPI * GETFINALPATHNAMEBYHANDLEPTR)(
HANDLE hFile,
@@ -1393,58 +1805,84 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
GETFINALPATHNAMEBYHANDLEPTR pGetFinalPathNameByHandle =
(GETFINALPATHNAMEBYHANDLEPTR)GetProcAddress(hModule, "GetFinalPathNameByHandleW");
- if (pGetFinalPathNameByHandle == NULL) {
- FreeLibrary(hModule);
- } else {
+ if (pGetFinalPathNameByHandle != NULL) {
+ DWORD fileAttributes;
+ ensure_wpath(state, &wname);
/* first check if file is a symlink; {error, einval} otherwise */
- DWORD fileAttributes = GetFileAttributesW(wname);
+ fileAttributes = GetFileAttributesW(wname);
if ((fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
- BOOLEAN success = 0;
+ DWORD success = 0;
HANDLE h = CreateFileW(wname, GENERIC_READ, FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
int len;
if(h != INVALID_HANDLE_VALUE) {
- success = pGetFinalPathNameByHandle(h, wbuffer, size / sizeof(WCHAR),0);
- /* GetFinalPathNameByHandle prepends path with "\\?\": */
- len = wcslen(wbuffer);
- wmemmove(wbuffer,wbuffer+4,len-3);
- if (len - 4 >= 2 && wbuffer[1] == L':' && wbuffer[0] >= L'A' &&
- wbuffer[0] <= L'Z') {
- wbuffer[0] = wbuffer[0] + L'a' - L'A';
+ if (!wbuffer) { /* dynamic allocation */
+ WCHAR dummy;
+ wsize = pGetFinalPathNameByHandle(h, &dummy, 0, 0);
+ if (wsize) {
+ wbuffer = wpath_tmp_alloc(state, wsize);
+ }
}
+ if (wbuffer
+ && (success = pGetFinalPathNameByHandle(h, wbuffer, wsize, 0))
+ && success < wsize) {
+ WCHAR* wp;
+
+ /* GetFinalPathNameByHandle prepends path with "\\?\": */
+ len = wcslen(wbuffer);
+ wmemmove(wbuffer,wbuffer+4,len-3);
+ if (len - 4 >= 2 && wbuffer[1] == L':' && wbuffer[0] >= L'A' &&
+ wbuffer[0] <= L'Z') {
+ wbuffer[0] = wbuffer[0] + L'a' - L'A';
+ }
- for ( ; *wbuffer; wbuffer++)
- if (*wbuffer == L'\\')
- *wbuffer = L'/';
+ for (wp=wbuffer ; *wp; wp++)
+ if (*wp == L'\\')
+ *wp = L'/';
+ }
CloseHandle(h);
- }
- FreeLibrary(hModule);
+ }
if (success) {
- return 1;
+ ret = (char*) wbuffer;
} else {
- return set_error(errInfo);
+ set_error(state->errInfo);
}
} else {
- FreeLibrary(hModule);
errno = EINVAL;
- return check_error(-1, errInfo);
+ save_last_error(state->errInfo);
}
+ goto done;
}
}
errno = ENOTSUP;
- return check_error(-1, errInfo);
+ save_last_error(state->errInfo);
+
+done:
+ if (hModule)
+ FreeLibrary(hModule);
+ return ret;
}
int
efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size)
{
+ Efile_call_state state;
+ int ret;
+ DBG_TRACE(1, orig_name);
+ call_state_init(&state, errInfo);
+ ret = do_altname(&state, orig_name, buffer, size);
+ call_state_free(&state);
+ return ret;
+}
+
+static int
+do_altname(Efile_call_state* state, char* orig_name, char* buffer, size_t size)
+{
WIN32_FIND_DATAW wfd;
HANDLE fh;
- WCHAR name[_MAX_PATH+1];
+ WCHAR* name;
int name_len;
- WCHAR* path;
- WCHAR pathbuf[_MAX_PATH+1]; /* Unclear weather GetCurrentDirectory will access one char after
- _MAX_PATH */
+ WCHAR* full_path = NULL;
WCHAR *worig_name = (WCHAR *) orig_name;
WCHAR *wbuffer = (WCHAR *) buffer;
int drive; /* Drive for filename (1 = A:, 2 = B: etc). */
@@ -1453,8 +1891,8 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size)
if (wcspbrk(worig_name, L"?*")) {
enoent:
- errInfo->posix_errno = ENOENT;
- errInfo->os_errno = ERROR_FILE_NOT_FOUND;
+ state->errInfo->posix_errno = ENOENT;
+ state->errInfo->os_errno = ERROR_FILE_NOT_FOUND;
return 0;
}
@@ -1462,24 +1900,23 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size)
* Move the name to a buffer and make sure to remove a trailing
* slash, because it causes FindFirstFile() to fail on Win95.
*/
-
- if ((name_len = wcslen(worig_name)) >= _MAX_PATH) {
- goto enoent;
- } else {
- wcscpy(name, worig_name);
- if (name_len > 2 && ISSLASH(name[name_len-1]) &&
- name[name_len-2] != L':') {
- name[name_len-1] = L'\0';
- }
+ ensure_wpath(state, &worig_name);
+ name_len = wcslen(worig_name);
+
+ name = wpath_tmp_alloc(state, name_len + 1);
+ wcscpy(name, worig_name);
+ if (name_len > 2 && ISSLASH(name[name_len-1]) &&
+ name[name_len-2] != L':') {
+ name[name_len-1] = L'\0';
}
/* Try to get disk from name. If none, get current disk. */
if (name[1] != L':') {
+ WCHAR* cwd_path = get_cwd_wpath_tmp(state);
drive = 0;
- if (GetCurrentDirectoryW(_MAX_PATH, pathbuf) &&
- pathbuf[1] == L':') {
- drive = towlower(pathbuf[0]) - L'a' + 1;
+ if (cwd_path[1] == L':') {
+ drive = towlower(cwd_path[0]) - L'a' + 1;
}
} else if (*name && name[2] == L'\0') {
/*
@@ -1491,13 +1928,15 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size)
}
fh = FindFirstFileW(name,&wfd);
if (fh == INVALID_HANDLE_VALUE) {
+ DWORD fff_error = GetLastError();
if (!(wcspbrk(name, L"./\\") &&
- (path = _wfullpath(pathbuf, name, _MAX_PATH)) &&
+ (full_path = get_full_wpath_tmp(state, name, NULL, 0)) &&
/* root dir. ('C:\') or UNC root dir. ('\\server\share\') */
- ((wcslen(path) == 3) || is_root_unc_name(path)) &&
- (GetDriveTypeW(path) > 1) ) ) {
- errno = errno_map(GetLastError());
- return check_error(-1, errInfo);
+ ((wcslen(full_path) == 3) || is_root_unc_name(full_path)) &&
+ (GetDriveTypeW(full_path) > 1) ) ) {
+
+ set_os_errno(state->errInfo, fff_error);
+ return 0;
}
/*
* Root directories (such as C:\ or \\server\share\ are fabricated.
@@ -1518,17 +1957,37 @@ efile_altname(Efile_error* errInfo, char* orig_name, char* buffer, size_t size)
int
efile_link(Efile_error* errInfo, char* old, char* new)
{
+ Efile_call_state state;
WCHAR *wold = (WCHAR *) old;
WCHAR *wnew = (WCHAR *) new;
+ int ret;
+ DBG_TRACE(1, old);
+ call_state_init(&state, errInfo);
+ ensure_wpath(&state, &wold);
+ ensure_wpath(&state, &wnew);
if(!CreateHardLinkW(wnew, wold, NULL)) {
- return set_error(errInfo);
+ ret = set_error(errInfo);
}
- return 1;
+ else ret =1;
+ call_state_free(&state);
+ return ret;
}
int
efile_symlink(Efile_error* errInfo, char* old, char* new)
{
+ Efile_call_state state;
+ int ret;
+ DBG_TRACE2(1, "symlink(%s <- %s)", old, new);
+ call_state_init(&state, errInfo);
+ ret = do_symlink(&state, old, new);
+ call_state_free(&state);
+ return ret;
+}
+
+static int
+do_symlink(Efile_call_state* state, char* old, char* new)
+{
/*
* Load dll and see if we have CreateSymbolicLink at runtime:
* (Vista only)
@@ -1536,6 +1995,8 @@ efile_symlink(Efile_error* errInfo, char* old, char* new)
HINSTANCE hModule = NULL;
WCHAR *wold = (WCHAR *) old;
WCHAR *wnew = (WCHAR *) new;
+
+ DBG_TRACE(1, old);
if ((hModule = LoadLibrary("kernel32.dll")) != NULL) {
typedef BOOLEAN (WINAPI * CREATESYMBOLICLINKFUNCPTR) (
LPCWSTR lpSymlinkFileName,
@@ -1547,6 +2008,9 @@ efile_symlink(Efile_error* errInfo, char* old, char* new)
"CreateSymbolicLinkW");
/* A for MBCS, W for UNICODE... char* above implies 'W'! */
if (pCreateSymbolicLink != NULL) {
+ ensure_wpath(state, &wold);
+ ensure_wpath(state, &wnew);
+ {
DWORD attr = GetFileAttributesW(wold);
int flag = (attr != INVALID_FILE_ATTRIBUTES &&
attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
@@ -1557,19 +2021,21 @@ efile_symlink(Efile_error* errInfo, char* old, char* new)
if (success) {
return 1;
} else {
- return set_error(errInfo);
+ return set_error(state->errInfo);
}
+ }
} else
FreeLibrary(hModule);
}
errno = ENOTSUP;
- return check_error(-1, errInfo);
+ return check_error(-1, state->errInfo);
}
int
efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
Sint64 length, int advise)
{
+ DBG_TRACE(2, L"");
/* posix_fadvise is not available on Windows, do nothing */
errno = ERROR_SUCCESS;
return check_error(0, errInfo);
@@ -1578,6 +2044,7 @@ efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
int
efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length)
{
+ DBG_TRACE(2, L"");
/* No file preallocation method available in Windows. */
errno = errno_map(ERROR_NOT_SUPPORTED);
SetLastError(ERROR_NOT_SUPPORTED);
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index fa99c817f0..2497d51df1 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -1101,9 +1101,9 @@ BIF_RETTYPE hipe_bifs_make_fun_3(BIF_ALIST_3)
#endif
/*
- * args: Nativecodeaddress, Module, {Uniq, Index, BeamAddress}
+ * args: Module, {Uniq, Index, BeamAddress}
*/
-BIF_RETTYPE hipe_bifs_make_fe_3(BIF_ALIST_3)
+BIF_RETTYPE hipe_bifs_get_fe_2(BIF_ALIST_2)
{
Eterm mod;
Uint index;
@@ -1111,20 +1111,15 @@ BIF_RETTYPE hipe_bifs_make_fe_3(BIF_ALIST_3)
void *beam_address;
ErlFunEntry *fe;
Eterm *tp;
- void *native_address;
-
- native_address = term_to_address(BIF_ARG_1);
- if (!native_address)
- BIF_ERROR(BIF_P, BADARG);
- if (is_not_atom(BIF_ARG_2))
+ if (is_not_atom(BIF_ARG_1))
BIF_ERROR(BIF_P, BADARG);
- mod = BIF_ARG_2;
+ mod = BIF_ARG_1;
- if (is_not_tuple(BIF_ARG_3) ||
- (arityval(*tuple_val(BIF_ARG_3)) != 3))
+ if (is_not_tuple(BIF_ARG_2) ||
+ (arityval(*tuple_val(BIF_ARG_2)) != 3))
BIF_ERROR(BIF_P, BADARG);
- tp = tuple_val(BIF_ARG_3);
+ tp = tuple_val(BIF_ARG_2);
if (term_to_Uint(tp[1], &uniq) == 0)
BIF_ERROR(BIF_P, BADARG);
if (term_to_Uint(tp[2], &index) == 0)
@@ -1144,10 +1139,28 @@ BIF_RETTYPE hipe_bifs_make_fe_3(BIF_ALIST_3)
printf("no fun entry for %s %ld:%ld\n", atom_buf, uniq, index);
BIF_ERROR(BIF_P, BADARG);
}
+ BIF_RET(address_to_term((void *)fe, BIF_P));
+}
+
+/*
+ * args: FE, Nativecodeaddress
+ */
+BIF_RETTYPE hipe_bifs_set_native_address_in_fe_2(BIF_ALIST_2)
+{
+ ErlFunEntry *fe;
+ void *native_address;
+
+ fe = (ErlFunEntry *)term_to_address(BIF_ARG_1);
+ if (!fe)
+ BIF_ERROR(BIF_P, BADARG);
+ native_address = term_to_address(BIF_ARG_2);
+ if (!native_address)
+ BIF_ERROR(BIF_P, BADARG);
+
fe->native_address = native_address;
if (erts_refc_dectest(&fe->refc, 0) == 0)
erts_erase_fun_entry(fe);
- BIF_RET(address_to_term((void *)fe, BIF_P));
+ BIF_RET(am_true);
}
#if 0 /* XXX: unused */
diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab
index ce641365e9..2514b1c3a5 100644
--- a/erts/emulator/hipe/hipe_bif0.tab
+++ b/erts/emulator/hipe/hipe_bif0.tab
@@ -69,7 +69,8 @@ bif hipe_bifs:atom_to_word/1
bif hipe_bifs:term_to_word/1
#bif hipe_bifs:make_fun/3
-bif hipe_bifs:make_fe/3
+bif hipe_bifs:get_fe/2
+bif hipe_bifs:set_native_address_in_fe/2
#bif hipe_bifs:make_native_stub/2
bif hipe_bifs:find_na_or_make_stub/2
diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c
index 7637049bc3..054911e822 100644
--- a/erts/emulator/hipe/hipe_bif2.c
+++ b/erts/emulator/hipe/hipe_bif2.c
@@ -182,3 +182,10 @@ BIF_RETTYPE hipe_bifs_debug_native_called_2(BIF_ALIST_2)
BIF_RET(am_ok);
}
+/* Stub-BIF for LLVM:
+ * Reloads BP, SP (in llvm unwind label) */
+
+BIF_RETTYPE hipe_bifs_llvm_fix_pinned_regs_0(BIF_ALIST_0)
+{
+ BIF_RET(am_ok);
+}
diff --git a/erts/emulator/hipe/hipe_bif2.tab b/erts/emulator/hipe/hipe_bif2.tab
index 45a395bf57..1b659cfa90 100644
--- a/erts/emulator/hipe/hipe_bif2.tab
+++ b/erts/emulator/hipe/hipe_bif2.tab
@@ -30,3 +30,4 @@ bif hipe_bifs:in_native/0
bif hipe_bifs:modeswitch_debug_on/0
bif hipe_bifs:modeswitch_debug_off/0
bif hipe_bifs:debug_native_called/2
+bif hipe_bifs:llvm_fix_pinned_regs/0
diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4
index 0997d81b2f..5f92b6bac4 100644
--- a/erts/emulator/hipe/hipe_bif_list.m4
+++ b/erts/emulator/hipe/hipe_bif_list.m4
@@ -268,9 +268,16 @@ noproc_primop_interface_1(nbif_atomic_inc, hipe_atomic_inc)
*/
define(CFUN,`ifelse($1,term_to_binary_1,hipe_wrapper_term_to_binary_1,
ifelse($1,term_to_binary_2,hipe_wrapper_term_to_binary_2,
-ifelse($1,erts_internal_binary_to_term_1,hipe_wrapper_erts_internal_binary_to_term_1,
-ifelse($1,erts_internal_binary_to_term_2,hipe_wrapper_erts_internal_binary_to_term_2,
-$1))))')
+ifelse($1,binary_to_term_1,hipe_wrapper_binary_to_term_1,
+ifelse($1,binary_to_term_2,hipe_wrapper_binary_to_term_2,
+ifelse($1,binary_to_list_1,hipe_wrapper_binary_to_list_1,
+ifelse($1,binary_to_list_3,hipe_wrapper_binary_to_list_3,
+ifelse($1,bitstring_to_list_1,hipe_wrapper_bitstring_to_list_1,
+ifelse($1,list_to_binary_1,hipe_wrapper_list_to_binary_1,
+ifelse($1,iolist_to_binary_1,hipe_wrapper_iolist_to_binary_1,
+ifelse($1,binary_list_to_bin_1,hipe_wrapper_binary_list_to_bin_1,
+ifelse($1,list_to_bitstring_1,hipe_wrapper_list_to_bitstring_1,
+$1)))))))))))')
define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))')
include(TARGET/`erl_bif_list.h')
diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c
index 8f997aafab..f5668013e2 100644
--- a/erts/emulator/hipe/hipe_x86_signal.c
+++ b/erts/emulator/hipe/hipe_x86_signal.c
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -304,7 +304,9 @@ static void hipe_sigaltstack(void *ss_sp)
*/
void hipe_thread_signal_init(void)
{
- hipe_sigaltstack(erts_alloc(ERTS_ALC_T_HIPE, SIGSTKSZ));
+ /* Stack don't really need to be cache aligned.
+ We use it to suppress false leak report from valgrind */
+ hipe_sigaltstack(erts_alloc_permanent_cache_aligned(ERTS_ALC_T_HIPE, SIGSTKSZ));
}
#endif
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 245841a768..1db673e7f3 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -527,7 +527,8 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
/* fast track to stop_select callback */
stop_select_fn = prt->drv_ptr->stop_select;
#ifdef USE_VM_PROBES
- strncpy(name, prt->drv_ptr->name, sizeof(name)-1);
+ strncpy(name, prt->drv_ptr->name,
+ sizeof(DTRACE_CHARBUF_NAME(name))-1);
name[sizeof(name)-1] = '\0';
#endif
ret = 0;
diff --git a/erts/emulator/sys/ose/default.lmconf b/erts/emulator/sys/ose/beam.lmconf
index a66b0ece56..4ad46b01d9 100644
--- a/erts/emulator/sys/ose/default.lmconf
+++ b/erts/emulator/sys/ose/beam.lmconf
@@ -4,12 +4,13 @@ OSE_LM_POOL_SIZE=0x200000
OSE_LM_MAIN_NAME=main
OSE_LM_MAIN_STACK_SIZE=0xF000
OSE_LM_MAIN_PRIORITY=20
+## Has to be of a type that allows MAM
OSE_LM_PROGRAM_TYPE=APP_RAM
OSE_LM_DATA_INIT=YES
OSE_LM_BSS_INIT=YES
OSE_LM_EXEC_MODEL=SHARED
HEAP_MAX_SIZE=1000000000
-HEAP_SMALL_BUF_INIT_SIZE=64000000
+HEAP_SMALL_BUF_INIT_SIZE=20971520
HEAP_LARGE_BUF_THRESHOLD=16000000
HEAP_LOCK_TYPE=2
diff --git a/erts/emulator/sys/ose/erl_main.c b/erts/emulator/sys/ose/erl_main.c
index 03119c3fec..23a9bc93a4 100644
--- a/erts/emulator/sys/ose/erl_main.c
+++ b/erts/emulator/sys/ose/erl_main.c
@@ -30,6 +30,8 @@
int
main(int argc, char **argv) {
+ (void)stdin;(void)stdout;(void)stderr;
+
/* When starting using pm_create -c ARGV="-- -root ..", argv[0] is the first
part of ARGV and not the name of the executable. So we shuffle some
pointers here to make erl_start happy. */
diff --git a/erts/emulator/sys/ose/erl_poll.c b/erts/emulator/sys/ose/erl_poll.c
index ca1ed6e53a..7d2a3d1e0b 100644
--- a/erts/emulator/sys/ose/erl_poll.c
+++ b/erts/emulator/sys/ose/erl_poll.c
@@ -551,7 +551,12 @@ int erts_poll_wait(ErtsPollSet ps,
fd.id, fd.signo, current_process());
erts_send_error_to_logger_nogl(dsbufp);
timeout = 0;
- ASSERT(0);
+ /* Under normal circumstances the signal is deallocated by the
+ * driver that issued the select operation. But in this case
+ * there's no driver waiting for such signal so we have to
+ * deallocate it here */
+ if (sig)
+ free_buf(&sig);
} else {
int i;
struct erts_sys_fd_type *fd = NULL;
@@ -737,6 +742,7 @@ union SIGNAL *erl_drv_ose_get_signal(ErlDrvEvent drv_ev) {
ev->msgs = msg->next;
ethr_mutex_unlock(&ev->mtx);
erts_free(ERTS_ALC_T_FD_SIG_LIST,msg);
+ restore(sig);
return sig;
}
}
diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c
index c892cc69c7..5b950a7dae 100644
--- a/erts/emulator/sys/ose/sys.c
+++ b/erts/emulator/sys/ose/sys.c
@@ -195,7 +195,9 @@ static volatile int children_died;
write_buff += sizeof(struct aiocb *); \
memcpy(write_buff,BUFF,SIZE+1); \
SET_AIO(*write_req,FD,SIZE,write_buff); \
- aio_write(write_req); \
+ if (aio_write(write_req)) \
+ ramlog_printf("%s:%d: write failed with %d\n", \
+ __FILE__,__LINE__,errno); \
} \
} while(0)
@@ -210,13 +212,13 @@ static volatile int children_died;
driver_free(buffer_ptr); \
} while(0)
-/* When we have several schedulers, we need to make sure
- * that scheduler issuing aio_dispatch() is the owner on the signal */
#define DISPATCH_AIO(sig) do { \
- restore(sig); \
- aio_dispatch(sig); \
+ if (aio_dispatch(sig)) \
+ ramlog_printf("%s:%d: dispatch failed with %d\n", \
+ __FILE__,__LINE__,errno); \
} while(0)
+#define AIO_PIPE_SIZE 1024
/* debug print macros */
#define DEBUG_RES 0
@@ -371,6 +373,63 @@ thr_create_prepare_child(void *vtcdp)
#endif /* #ifdef USE_THREADS */
+/* The two functions below are stolen from win_con.c
+ They have to use malloc/free/realloc directly becasue
+ we want to do able to do erts_printf very early on.
+ */
+#define VPRINTF_BUF_INC_SIZE 128
+static erts_dsprintf_buf_t *
+grow_vprintf_buf(erts_dsprintf_buf_t *dsbufp, size_t need)
+{
+ char *buf;
+ size_t size;
+
+ ASSERT(dsbufp);
+
+ if (!dsbufp->str) {
+ size = (((need + VPRINTF_BUF_INC_SIZE - 1)
+ / VPRINTF_BUF_INC_SIZE)
+ * VPRINTF_BUF_INC_SIZE);
+ buf = (char *) malloc(size * sizeof(char));
+ }
+ else {
+ size_t free_size = dsbufp->size - dsbufp->str_len;
+
+ if (need <= free_size)
+ return dsbufp;
+
+ size = need - free_size + VPRINTF_BUF_INC_SIZE;
+ size = (((size + VPRINTF_BUF_INC_SIZE - 1)
+ / VPRINTF_BUF_INC_SIZE)
+ * VPRINTF_BUF_INC_SIZE);
+ size += dsbufp->size;
+ buf = (char *) realloc((void *) dsbufp->str,
+ size * sizeof(char));
+ }
+ if (!buf)
+ return NULL;
+ if (buf != dsbufp->str)
+ dsbufp->str = buf;
+ dsbufp->size = size;
+ return dsbufp;
+}
+
+static int erts_sys_ramlog_printf(char *format, va_list arg_list)
+{
+ int res,i;
+ erts_dsprintf_buf_t dsbuf = ERTS_DSPRINTF_BUF_INITER(grow_vprintf_buf);
+ res = erts_vdsprintf(&dsbuf, format, arg_list);
+ if (res >= 0) {
+ for (i = 0; i < dsbuf.str_len; i+= 50)
+ /* We print 50 characters at a time because otherwise
+ the ramlog looks broken */
+ ramlog_printf("%.*s",dsbuf.str_len-50 < 0?dsbuf.str_len:50,dsbuf.str+i);
+ }
+ if (dsbuf.str)
+ free((void *) dsbuf.str);
+ return res;
+}
+
void
erts_sys_pre_init(void)
{
@@ -409,6 +468,9 @@ erts_sys_pre_init(void)
children_died = 0;
#endif
#endif /* USE_THREADS */
+
+ erts_printf_stdout_func = erts_sys_ramlog_printf;
+
erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0);
}
@@ -650,7 +712,7 @@ static void stop_select(ErlDrvEvent, void*);
static PROCESS
get_signal_proxy_pid(void) {
union SIGNAL *sig;
- SIGSELECT any_sig[] = {0};
+ SIGSELECT any_sig[] = {1,ERTS_SIGNAL_OSE_DRV_ATTACH};
if (!sig_proxy_pid) {
sig = alloc(sizeof(union SIGNAL), ERTS_SIGNAL_OSE_DRV_ATTACH);
@@ -685,7 +747,7 @@ resolve_signal(union SIGNAL* sig) {
struct erl_drv_entry spawn_driver_entry = {
spawn_init,
spawn_start,
- erl_stop,
+ NULL, /* erl_stop, */
output,
ready_input,
ready_output,
@@ -784,7 +846,11 @@ set_driver_data(ErlDrvPort port_num,
/* READ */
if (read_write & DO_READ) {
- efs_examine_fd(ifd, FLIB_FD_HANDLE, &driver_data[ifd].handle, 0);
+ EfsStatus res = efs_examine_fd(ifd, FLIB_FD_HANDLE,
+ &driver_data[ifd].handle, 0);
+ if (res != EFS_SUCCESS)
+ ramlog_printf("%s:%d: efs_examine_fd(%d) failed with %d\n",
+ __FILE__,__LINE__,ifd,errno);
driver_data[ifd].ifd = ifd;
driver_data[ifd].packet_bytes = packet_bytes;
driver_data[ifd].port_num = port_num;
@@ -792,10 +858,9 @@ set_driver_data(ErlDrvPort port_num,
/* async read struct */
memset(&driver_data[ifd].aiocb, 0, sizeof(struct aiocb));
- driver_data[ifd].aiocb.aio_buf = driver_alloc(255);
+ driver_data[ifd].aiocb.aio_buf = driver_alloc(AIO_PIPE_SIZE);
driver_data[ifd].aiocb.aio_fildes = ifd;
- driver_data[ifd].aiocb.aio_nbytes = 255;
-
+ driver_data[ifd].aiocb.aio_nbytes = (packet_bytes?packet_bytes:AIO_PIPE_SIZE);
driver_data[ifd].alive = 1;
driver_data[ifd].status = 0;
driver_data[ifd].input_event =
@@ -826,7 +891,9 @@ set_driver_data(ErlDrvPort port_num,
(void) driver_select(port_num, driver_data[ifd].input_event,
(ERL_DRV_READ | ERL_DRV_USE), 1);
- aio_read(&driver_data[ifd].aiocb);
+ if (aio_read(&driver_data[ifd].aiocb))
+ ramlog_printf("%s:%d: aio_read(%d) failed with %d\n",
+ __FILE__,__LINE__,ifd,errno);
}
else { /* WRITE ONLY */
efs_examine_fd(ofd, FLIB_FD_HANDLE, &driver_data[ofd].handle, 0);
@@ -926,7 +993,7 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
{
int ifd[2];
int ofd[2];
- static uint32_t ticker = 0;
+ static uint32_t ticker = 1;
PmStatus pm_status;
OSDOMAIN domain = PM_NEW_DOMAIN;
PROCESS progpid, mainbid, mainpid;
@@ -938,39 +1005,53 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
int handle_size;
char *ptr;
- /* handle arguments */
- ptr = strchr(name, ' ');
- if (ptr != NULL) {
- *ptr ='\0';
- ptr++;
- args = ptr;
+
+ args = driver_alloc(strlen(name)+1);
+ strcpy(args, name);
+ /* We need to handle name in three parts
+ * - install handle (must be unique)
+ * - install binary (needed for ose_pm_install_load_module())
+ * - full path (as argument to the spawned applications env.var
+ */
+
+ /* full path including arguments */
+ args = driver_alloc(strlen(name)+1);
+ strcpy(args, name);
+
+ /* handle path */
+ tmp_handle = strrchr(name, '/');
+ if (tmp_handle == NULL) {
+ tmp_handle = name;
}
else {
- args = NULL;
+ tmp_handle++;
}
- /* create an install handle */
- ptr = strrchr(name, '/');
+ /* handle args */
+ ptr = strchr(tmp_handle, ' ');
if (ptr != NULL) {
- ptr++;
- tmp_handle = ptr;
+ *ptr = '\0';
+ handle_size = ptr - tmp_handle;
}
else {
- tmp_handle = name;
+ handle_size = strlen(name)+1;
}
- handle_size = strlen(tmp_handle)+1;
- handle_size += (ticker<10)?3:((ticker<100)?4:5);
+ /* make room for ticker */
+ handle_size += (ticker<10)?3:((ticker<100)?4:5);
handle = driver_alloc(handle_size);
- snprintf(handle, handle_size, "%s_%d", tmp_handle, ticker);
-
+
do {
- snprintf(handle, handle_size, "%s_%d", tmp_handle, ticker++);
+ snprintf(handle, handle_size, "%s_%d", tmp_handle, ticker);
pm_status = ose_pm_install_load_module(0, "ELF", name, handle,
0, 0, NULL);
-
+ ticker++;
} while (pm_status == PM_EINSTALL_HANDLE_ALREADY_INSTALLED);
- DEBUG_CHECK_RES(pm_status, PM_SUCCESS);
+
+ if (pm_status != PM_SUCCESS) {
+ errno = ENOSYS; /* FIXME add comment */
+ return ERL_DRV_ERROR_ERRNO;
+ }
/* Create Program */
pm_status = ose_pm_create_program(&domain, handle, 0, 0,
@@ -1143,17 +1224,13 @@ static void erl_stop(ErlDrvData drv_data)
if (data->ifd != data->ofd) { /* read and write */
nbio_stop_fd(data->port_num, data->input_event);
nbio_stop_fd(data->port_num, data->output_event);
- driver_select(data->port_num, data->input_event, ERL_DRV_USE, 0);
- driver_select(data->port_num, data->output_event, ERL_DRV_USE, 0);
}
else { /* write only */
nbio_stop_fd(data->port_num, data->output_event);
- driver_select(data->port_num, data->output_event, ERL_DRV_USE, 0);
}
}
else { /* read only */
nbio_stop_fd(data->port_num, data->input_event);
- driver_select(data->port_num, data->input_event, ERL_DRV_USE, 0);
}
close(data->ifd);
close(data->ofd);
@@ -1177,18 +1254,31 @@ static void output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len)
lbp = lb + (4-(data->packet_bytes));
if ((sz = driver_sizeq(data->port_num)) > 0) {
- driver_enq(data->port_num, lbp, data->packet_bytes);
- driver_enq(data->port_num, buf, len);
- if (sz + len + data->packet_bytes >= (1 << 13))
+ if (data->packet_bytes != 0) {
+ driver_enq(data->port_num, lbp, data->packet_bytes);
+ }
+ driver_enq(data->port_num, buf, len);
+
+ if (sz + len + data->packet_bytes >= (1 << 13))
set_busy_port(data->port_num, 1);
}
else {
- driver_enq(data->port_num, buf, len); /* n is the skip value */
-
+ char *pbbuf;
+ if (data->packet_bytes != 0) {
+ pbbuf = malloc(len + data->packet_bytes);
+ int i;
+ for (i = 0; i < data->packet_bytes; i++) {
+ *pbbuf++ = *lbp++;
+ }
+ strncpy(pbbuf, buf, len);
+ pbbuf -= data->packet_bytes;
+ }
driver_select(data->port_num, data->output_event,
ERL_DRV_WRITE|ERL_DRV_USE, 1);
-
- WRITE_AIO(data->ofd, len, buf);
+ WRITE_AIO(data->ofd,
+ (data->packet_bytes ? len+data->packet_bytes : len),
+ (data->packet_bytes ? pbbuf : buf));
+ if (data->packet_bytes != 0) free(pbbuf);
}
return; /* 0; */
}
@@ -1204,12 +1294,12 @@ static int port_inp_failure(ErlDrvPort port_num, ErlDrvEvent ready_fd, int res)
ASSERT(res <= 0);
erl_drv_ose_event_fetch(ready_fd,&sig_no, NULL, (void **)&fd);
-
/* As we need to handle two signals, we do this in two steps */
if (driver_data[*fd].alive) {
report_exit_status(driver_data[*fd].report_exit, 0); /* status? */
}
else {
+ driver_select(port_num,ready_fd,DO_READ|DO_WRITE,0);
clear_fd_data(*fd);
driver_report_exit(driver_data[*fd].port_num, driver_data[*fd].status);
/* As we do not really know if the spawn has crashed or exited nicely
@@ -1248,6 +1338,10 @@ static void ready_input(ErlDrvData drv_data, ErlDrvEvent ready_fd)
}
else {
res = sig->fm_read_reply.actual;
+ if (res == 0) {
+ port_inp_failure(data->port_num, ready_fd, res);
+ break;
+ }
if (data->packet_bytes == 0) {
if (res < 0) {
@@ -1258,6 +1352,7 @@ static void ready_input(ErlDrvData drv_data, ErlDrvEvent ready_fd)
else if (res == 0) {
/* read of 0 bytes, eof, otherside of pipe is assumed dead */
port_inp_failure(data->port_num, ready_fd, res);
+ break;
}
else {
buf = driver_alloc(res);
@@ -1267,100 +1362,91 @@ static void ready_input(ErlDrvData drv_data, ErlDrvEvent ready_fd)
driver_output(data->port_num, (char*) buf, res);
driver_free(buf);
}
+ /* clear the previous read */
+ memset(data->aiocb.aio_buf, 0, res);
+
+ /* issue a new read */
+ DISPATCH_AIO(sig);
+ aio_read(&data->aiocb);
}
- /* We try to read the remainder */
- else if (fd_data[data->ifd].remain > 0) {
- if (res < 0) {
- if ((errno != EINTR) && (errno != ERRNO_BLOCK)) {
- port_inp_failure(data->port_num, ready_fd, res);
+ else if (data->packet_bytes && fd_data[data->ifd].remain > 0) {
+ /* we've read a partial package, or a header */
+
+ if (res == fd_data[data->ifd].remain) { /* we are done! */
+ char *buf = data->aiocb.aio_buf;
+ int i;
+
+ /* do we have anything buffered? */
+ if (fd_data[data->ifd].buf != NULL) {
+ memcpy(fd_data[data->ifd].buf + fd_data[data->ifd].sz,
+ buf, res);
+ buf = fd_data[data->ifd].buf;
}
- }
- else if (res == 0) {
- port_inp_failure(data->port_num, ready_fd, res);
- }
- else if (res == fd_data[data->ifd].remain) { /* we're done */
- driver_output(data->port_num,
- fd_data[data->ifd].buf,
- fd_data[data->ifd].sz);
+
+ fd_data[data->ifd].sz += res;
+ driver_output(data->port_num, buf, (fd_data[data->ifd].sz>0?fd_data[data->ifd].sz:res));
clear_fd_data(data->ifd);
- }
- else { /* if (res < fd_data[fd].remain) */
- fd_data[data->ifd].cpos += res;
- fd_data[data->ifd].remain -= res;
- }
- }
- else if (fd_data[data->ifd].remain == 0) { /* clean fd */
- if (res < 0) {
- if ((errno != EINTR) && (errno != ERRNO_BLOCK)) {
- port_inp_failure(data->port_num, ready_fd, res);
+
+ /* clear the previous read */
+ memset(data->aiocb.aio_buf, 0, res);
+
+ /* issue a new read */
+ DISPATCH_AIO(sig);
+ data->aiocb.aio_nbytes = data->packet_bytes;
+
+ if (data->aiocb.aio_buf == NULL) {
+ port_inp_failure(data->port_num, ready_fd, -1);
}
+ aio_read(&data->aiocb);
}
- else if (res == 0) { /* eof */
- port_inp_failure(data->port_num, ready_fd, res);
- }
- else if (res < data->packet_bytes - fd_data[data->ifd].psz) {
- memcpy(fd_data[data->ifd].pbuf+fd_data[data->ifd].psz,
- (void *)data->aiocb.aio_buf, res);
- fd_data[data->ifd].psz += res;
- }
- else { /* if (res >= packet_bytes) */
- unsigned char* cpos = (unsigned char*)data->aiocb.aio_buf;
- int bytes_left = res;
-
- while (1) {
- int psz = fd_data[data->ifd].psz;
- char* pbp = fd_data[data->ifd].pbuf + psz;
-
- while (bytes_left && (psz < data->packet_bytes)) {
- *pbp++ = *cpos++;
- bytes_left--;
- psz++;
- }
-
- if (psz < data->packet_bytes) {
- fd_data[data->ifd].psz = psz;
- break;
- }
- fd_data[data->ifd].psz = 0;
-
- switch (data->packet_bytes) {
- case 1: h = get_int8(fd_data[data->ifd].pbuf); break;
- case 2: h = get_int16(fd_data[data->ifd].pbuf); break;
- case 4: h = get_int32(fd_data[data->ifd].pbuf); break;
- default: ASSERT(0); return; /* -1; */
- }
-
- if (h <= (bytes_left)) {
- driver_output(data->port_num, (char*) cpos, h);
- cpos += h;
- bytes_left -= h;
- continue;
- }
- else { /* The last message we got was split */
- char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
- if (!buf) {
- errno = ENOMEM;
- port_inp_failure(data->port_num, ready_fd, -1);
- }
- else {
- erts_smp_atomic_add_nob(&sys_misc_mem_sz, h);
- sys_memcpy(buf, cpos, bytes_left);
- fd_data[data->ifd].buf = buf;
- fd_data[data->ifd].sz = h;
- fd_data[data->ifd].remain = h - bytes_left;
- fd_data[data->ifd].cpos = buf + bytes_left;
- }
- break;
- }
+ else if(res < fd_data[data->ifd].remain) { /* received part of a package */
+ if (fd_data[data->ifd].sz == 0) {
+
+ fd_data[data->ifd].sz += res;
+ memcpy(fd_data[data->ifd].buf, data->aiocb.aio_buf, res);
+ fd_data[data->ifd].remain -= res;
+ }
+ else {
+ memcpy(fd_data[data->ifd].buf + fd_data[data->ifd].sz,
+ data->aiocb.aio_buf, res);
+ fd_data[data->ifd].sz += res;
+ fd_data[data->ifd].remain -= res;
+ }
+ /* clear the previous read */
+ memset(data->aiocb.aio_buf, 0, res);
+
+ /* issue a new read */
+ DISPATCH_AIO(sig);
+ data->aiocb.aio_nbytes = fd_data[data->ifd].remain;
+
+ if (data->aiocb.aio_buf == NULL) {
+ port_inp_failure(data->port_num, ready_fd, -1);
}
+ aio_read(&data->aiocb);
}
}
+ else if (data->packet_bytes && fd_data[data->ifd].remain == 0) { /* we've recieved a header */
+
+ /* analyze the header FIXME */
+ switch (data->packet_bytes) {
+ case 1: h = get_int8(data->aiocb.aio_buf); break;
+ case 2: h = get_int16(data->aiocb.aio_buf); break;
+ case 4: h = get_int32(data->aiocb.aio_buf); break;
+ }
- /* reset the read buffer and init next asynch read */
- DISPATCH_AIO(sig);
- memset((void *)data->aiocb.aio_buf, 0, 255);
+ fd_data[data->ifd].buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h + data->packet_bytes);
+ fd_data[data->ifd].remain = ((h + data->packet_bytes) - res);
- if (res > 0) {
+ /* clear the previous read */
+ memset(data->aiocb.aio_buf, 0, data->packet_bytes);
+
+ /* issue a new read */
+ DISPATCH_AIO(sig);
+ data->aiocb.aio_nbytes = h;
+
+ if (data->aiocb.aio_buf == NULL) {
+ port_inp_failure(data->port_num, ready_fd, -1);
+ }
aio_read(&data->aiocb);
}
}
@@ -1400,11 +1486,16 @@ static void ready_output(ErlDrvData drv_data, ErlDrvEvent ready_fd)
DISPATCH_AIO(sig);
FREE_AIO(sig->fm_write_reply.buffer);
res = driver_deq(data->port_num, iov[0].iov_len);
- if (res > 0) {
+ if (res > 0) {
iov = driver_peekq(data->port_num, &vlen);
WRITE_AIO(data->ofd, iov[0].iov_len, iov[0].iov_base);
}
}
+ else if (vlen == 0) {
+ DISPATCH_AIO(sig);
+ FREE_AIO(sig->fm_write_reply.buffer);
+ }
+
}
sig = erl_drv_ose_get_signal(ready_fd);
}
@@ -1650,10 +1741,10 @@ erl_assert_error(const char* expr, const char* func,
{
fflush(stdout);
fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n",
- file, func, line, expr);
+ file, line, func, expr);
fflush(stderr);
ramlog_printf("%s:%d:%s() Assertion failed: %s\n",
- file, func, line, expr);
+ file, line, func, expr);
abort();
}
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
index 7c6e4a2f37..94eb6b1547 100644
--- a/erts/emulator/sys/unix/erl_child_setup.c
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -54,6 +54,17 @@ void sys_sigrelease(int sig)
#endif /* !SIG_SIGNAL */
#endif /* !SIG_SIGSET */
+#if defined(__ANDROID__)
+int __system_properties_fd(void);
+#endif /* __ANDROID__ */
+
+#if defined(__ANDROID__)
+#define SHELL "/system/bin/sh"
+#else
+#define SHELL "/bin/sh"
+#endif /* __ANDROID__ */
+
+
int
main(int argc, char *argv[])
{
@@ -89,8 +100,23 @@ main(int argc, char *argv[])
if (sscanf(argv[CS_ARGV_FD_CR_IX], "%d:%d", &from, &to) != 2)
return 1;
+
+#if defined(__ANDROID__)
+ for (i = from; i <= to; i++) {
+ if (i!=__system_properties_fd)
+ (void) close(i);
+ }
+#else
for (i = from; i <= to; i++)
(void) close(i);
+#endif /* __ANDROID__ */
+
+#if defined(HAVE_CLOSEFROM)
+ closefrom(from);
+#else
+ for (i = from; i <= to; i++)
+ (void) close(i);
+#endif
if (!(argv[CS_ARGV_WD_IX][0] == '.' && argv[CS_ARGV_WD_IX][1] == '\0')
&& chdir(argv[CS_ARGV_WD_IX]) < 0)
@@ -116,7 +142,25 @@ main(int argc, char *argv[])
execv(argv[CS_ARGV_NO_OF_ARGS],&(argv[CS_ARGV_NO_OF_ARGS + 1]));
}
} else {
- execl("/bin/sh", "sh", "-c", argv[CS_ARGV_CMD_IX], (char *) NULL);
+ execl(SHELL, "sh", "-c", argv[CS_ARGV_CMD_IX], (char *) NULL);
}
return 1;
}
+
+
+
+#if defined(__ANDROID__)
+int __system_properties_fd(void)
+{
+ int s, fd;
+ char *env;
+
+ env = getenv("ANDROID_PROPERTY_WORKSPACE");
+ if (!env) {
+ return -1;
+ }
+ fd = atoi(env);
+ return fd;
+}
+#endif /* __ANDROID__ */
+
diff --git a/erts/emulator/sys/unix/erl_unix_sys_ddll.c b/erts/emulator/sys/unix/erl_unix_sys_ddll.c
index 8760b58839..2659d623c7 100644
--- a/erts/emulator/sys/unix/erl_unix_sys_ddll.c
+++ b/erts/emulator/sys/unix/erl_unix_sys_ddll.c
@@ -123,6 +123,7 @@ int erts_sys_ddll_open(const char *full_name, void **handle, ErtsSysDdllError* e
int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err)
{
+#if defined(HAVE_DLOPEN)
int ret = ERL_DE_NO_ERROR;
char *str;
dlerror();
@@ -148,6 +149,9 @@ int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err)
ret = ERL_DE_DYNAMIC_ERROR_OFFSET - find_errcode(str, err);
}
return ret;
+#else
+ return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY;
+#endif
}
/*
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 865cb50a56..c3d7440409 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -149,6 +149,13 @@ extern void erl_crash_dump(char* file, int line, char* fmt, ...);
#define DIR_SEPARATOR_CHAR '/'
+#if defined(__ANDROID__)
+#define SHELL "/system/bin/sh"
+#else
+#define SHELL "/bin/sh"
+#endif /* __ANDROID__ */
+
+
#if defined(DEBUG)
#define ERL_BUILD_TYPE_MARKER ".debug"
#elif defined(PURIFY)
@@ -1596,7 +1603,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
}
}
} else {
- execle("/bin/sh", "sh", "-c", cmd_line, (char *) NULL, new_environ);
+ execle(SHELL, "sh", "-c", cmd_line, (char *) NULL, new_environ);
}
child_error:
_exit(1);
@@ -1717,7 +1724,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
fcntl(i, F_SETFD, 1);
qnx_spawn_options.flags = _SPAWN_SETSID;
- if ((pid = spawnl(P_NOWAIT, "/bin/sh", "/bin/sh", "-c", cmd_line,
+ if ((pid = spawnl(P_NOWAIT, SHELL, SHELL, "-c", cmd_line,
(char *) 0)) < 0) {
erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
reset_qnx_spawn();
diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h
index 8015e8f378..a78dbf64af 100644
--- a/erts/emulator/sys/win32/erl_win_sys.h
+++ b/erts/emulator/sys/win32/erl_win_sys.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2012. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -60,16 +60,18 @@
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
-/*
- * Define MAXPATHLEN in terms of MAXPATH if available.
- */
-
-#ifndef MAXPATH
-#define MAXPATH MAX_PATH
-#endif /* MAXPATH */
#ifndef MAXPATHLEN
-#define MAXPATHLEN MAXPATH
+#define MAXPATHLEN 4096
+/*
+ erts-6.0 (OTP 17.0):
+ We now accept windows paths longer than 260 (MAX_PATH) by conversion to
+ UNC path format. In order to also return long paths from the driver we
+ increased MAXPATHLEN from 260 to larger (but arbitrary) value 4096.
+ It would of course be nicer to instead dynamically allocate large enough
+ tmp buffers when efile_drv needs to return really long paths, and do that
+ for unix as well.
+ */
#endif /* MAXPATHLEN */
/*
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 938aac6a0e..44e9e4f243 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -58,13 +58,14 @@
ordering/1,unaligned_order/1,gc_test/1,
bit_sized_binary_sizes/1,
otp_6817/1,deep/1,obsolete_funs/1,robustness/1,otp_8117/1,
- otp_8180/1, trapping/1]).
+ otp_8180/1, trapping/1, large/1,
+ error_after_yield/1, cmp_old_impl/1]).
%% Internal exports.
-export([sleeper/0,trapping_loop/4]).
suite() -> [{ct_hooks,[ts_install_cth]},
- {timetrap,{minutes,2}}].
+ {timetrap,{minutes,4}}].
all() ->
[copy_terms, conversions, deep_lists, deep_bitstr_lists,
@@ -76,7 +77,8 @@ all() ->
bad_term_to_binary, more_bad_terms, otp_5484, otp_5933,
ordering, unaligned_order, gc_test,
bit_sized_binary_sizes, otp_6817, otp_8117, deep,
- obsolete_funs, robustness, otp_8180, trapping].
+ obsolete_funs, robustness, otp_8180, trapping, large,
+ error_after_yield, cmp_old_impl].
groups() ->
[].
@@ -1266,7 +1268,7 @@ deep(Config) when is_list(Config) ->
deep_roundtrip(T) ->
B = term_to_binary(T),
- T = binary_to_term_stress(B).
+ T = binary_to_term(B).
obsolete_funs(Config) when is_list(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
@@ -1351,7 +1353,16 @@ trapping(Config) when is_list(Config)->
do_trapping(5, term_to_binary,
fun() -> [lists:duplicate(2000000,2000000)] end),
do_trapping(5, binary_to_term,
- fun() -> [term_to_binary(lists:duplicate(2000000,2000000))] end).
+ fun() -> [term_to_binary(lists:duplicate(2000000,2000000))] end),
+ do_trapping(5, binary_to_list,
+ fun() -> [list_to_binary(lists:duplicate(2000000,$x))] end),
+ do_trapping(5, list_to_binary,
+ fun() -> [lists:duplicate(2000000,$x)] end),
+ do_trapping(5, bitstring_to_list,
+ fun() -> [list_to_bitstring([lists:duplicate(2000000,$x),<<7:4>>])] end),
+ do_trapping(5, list_to_bitstring,
+ fun() -> [[lists:duplicate(2000000,$x),<<7:4>>]] end)
+ .
do_trapping(0, _, _) ->
ok;
@@ -1384,9 +1395,189 @@ trapping_loop2(Bif,Args,N) ->
apply(erlang,Bif,Args),
trapping_loop2(Bif, Args, N-1).
+large(Config) when is_list(Config) ->
+ List = lists:flatten(lists:map(fun (_) ->
+ [0,1,2,3,4,5,6,7,8]
+ end,
+ lists:seq(1, 131072))),
+ Bin = list_to_binary(List),
+ List = binary_to_list(Bin),
+ PartList = lists:reverse(tl(tl(lists:reverse(tl(tl(List)))))),
+ PartList = binary_to_list(Bin, 3, length(List)-2),
+ ListBS = List ++ [<<7:4>>],
+ ListBS = bitstring_to_list(list_to_bitstring(ListBS)),
+ BitStr1 = list_to_bitstring(lists:duplicate(1024*1024, [<<1,5:3>>])),
+ BitStr1 = list_to_bitstring(bitstring_to_list(BitStr1)),
+ BitStr2 = list_to_bitstring([lists:duplicate(512*1024, [<<1,5:3>>]),
+ Bin]),
+ BitStr2 = list_to_bitstring(bitstring_to_list(BitStr2)),
+ ok.
+
+error_after_yield(Config) when is_list(Config) ->
+ L2BTrap = {erts_internal, list_to_binary_continue, 1},
+ error_after_yield(badarg, erlang, list_to_binary, 1, fun () -> [[mk_list(1000000), oops]] end, L2BTrap),
+ error_after_yield(badarg, erlang, iolist_to_binary, 1, fun () -> [[list2iolist(mk_list(1000000)), oops]] end, L2BTrap),
+ error_after_yield(badarg, erlang, list_to_bitstring, 1, fun () -> [[list2bitstrlist(mk_list(1000000)), oops]] end, L2BTrap),
+ error_after_yield(badarg, binary, list_to_bin, 1, fun () -> [[mk_list(1000000), oops]] end, L2BTrap),
+
+ B2TTrap = {erts_internal, binary_to_term_trap, 1},
+
+ error_after_yield(badarg, erlang, binary_to_term, 1, fun () -> [error_after_yield_bad_ext_term()] end, B2TTrap),
+ error_after_yield(badarg, erlang, binary_to_term, 2, fun () -> [error_after_yield_bad_ext_term(), [safe]] end, B2TTrap),
+
+ case erlang:system_info(wordsize) of
+ 4 ->
+ SysLimitSz = 1 bsl 32,
+ error_after_yield(system_limit, erlang, list_to_binary, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap),
+ error_after_yield(system_limit, erlang, iolist_to_binary, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap),
+ error_after_yield(system_limit, erlang, list_to_bitstring, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap),
+ error_after_yield(system_limit, binary, list_to_bin, 1, fun () -> [[huge_iolist(SysLimitSz), $x]] end, L2BTrap);
+ 8 ->
+ % Takes waaaay to long time to test system_limit on 64-bit archs...
+ ok
+ end,
+ ok.
+
+error_after_yield(Type, M, F, AN, AFun, TrapFunc) ->
+ io:format("Testing ~p for ~p:~p/~p~n", [Type, M, F, AN]),
+ Tracer = self(),
+ {Pid, Mon} = spawn_monitor(fun () ->
+ A = AFun(),
+ try
+ erlang:yield(),
+ erlang:trace(self(),true,[running,{tracer,Tracer}]),
+ apply(M, F, A),
+ exit({unexpected_success, {M, F, A}})
+ catch
+ error:Type ->
+ erlang:trace(self(),false,[running,{tracer,Tracer}]),
+ %% We threw the exception from the native
+ %% function we trapped to, but we want
+ %% the BIF that originally was called
+ %% to appear in the stack trace.
+ [{M, F, A, _} | _] = erlang:get_stacktrace()
+ end
+ end),
+ receive
+ {'DOWN', Mon, process, Pid, Reason} ->
+ normal = Reason
+ end,
+ TD = erlang:trace_delivered(Pid),
+ receive
+ {trace_delivered, Pid, TD} ->
+ NoYields = error_after_yield_sched(Pid, TrapFunc, 0),
+ io:format("No of yields: ~p~n", [NoYields]),
+ true = NoYields > 2
+ end,
+ ok.
+
+error_after_yield_sched(P, TrapFunc, N) ->
+ receive
+ {trace, P, out, TrapFunc} ->
+ receive
+ {trace, P, in, TrapFunc} ->
+ error_after_yield_sched(P, TrapFunc, N+1)
+ after 0 ->
+ exit(trap_sched_mismatch)
+ end;
+ {trace, P, out, Func} ->
+ receive
+ {trace, P, in, Func} ->
+ error_after_yield_sched(P, TrapFunc, N)
+ after 0 ->
+ exit(other_sched_mismatch)
+ end
+ after 0 ->
+ N
+ end.
+
+error_after_yield_bad_ext_term() ->
+ TupleSz = 2000000,
+ <<131, % Version magic
+ AtomExt/binary>> = term_to_binary(an_atom_we_use_for_this),
+ BadAtomExt = [100, %% ATOM_EXT
+ 255, 255, % Invalid size of 65535 bytes
+ "oops"],
+
+ %% Produce a large tuple where the last element is invalid
+ list_to_binary([131, %% Version magic
+ 105, %% LARGE_TUPLE_EXT
+ <<TupleSz:32/big>>, %% Tuple size
+ lists:duplicate(TupleSz-1, AtomExt), %% Valid atoms
+ BadAtomExt]). %% Invalid atom at the end
+
+cmp_old_impl(Config) when is_list(Config) ->
+ %% Compare results from new yielding implementations with
+ %% old non yielding implementations
+ Cookie = atom_to_list(erlang:get_cookie()),
+ Rel = "r16b_latest",
+ case test_server:is_release_available(Rel) of
+ false ->
+ {skipped, "No "++Rel++" available"};
+ true ->
+ {ok, Node} = ?t:start_node(list_to_atom(atom_to_list(?MODULE)++"_"++Rel),
+ peer,
+ [{args, " -setcookie "++Cookie},
+ {erl, [{release, Rel}]}]),
+
+ cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(1))]}),
+ cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(10))]}),
+ cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(100))]}),
+ cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(1000))]}),
+ cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(10000))]}),
+ cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(100000))]}),
+ cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(1000000))]}),
+ cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list(10000000))]}),
+ cmp_node(Node, {erlang, list_to_binary, [list2iolist(mk_list_lb(10000000))]}),
+
+ cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(1))]}),
+ cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(10))]}),
+ cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(100))]}),
+ cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(1000))]}),
+ cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(10000))]}),
+ cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(100000))]}),
+ cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(1000000))]}),
+ cmp_node(Node, {erlang, binary_to_list, [list_to_binary(mk_list(10000000))]}),
+
+ cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(1))]}),
+ cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(10))]}),
+ cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(100))]}),
+ cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(1000))]}),
+ cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(10000))]}),
+ cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(100000))]}),
+ cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(1000000))]}),
+ cmp_node(Node, {erlang, list_to_bitstring, [list2bitstrlist(mk_list(10000000))]}),
+
+ cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(1)))]}),
+ cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(10)))]}),
+ cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(100)))]}),
+ cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(1000)))]}),
+ cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(10000)))]}),
+ cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(100000)))]}),
+ cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(1000000)))]}),
+ cmp_node(Node, {erlang, bitstring_to_list, [list_to_bitstring(list2bitstrlist(mk_list(10000000)))]}),
+
+ ?t:stop_node(Node),
+
+ ok
+ end.
%% Utilities.
+huge_iolist(Lim) ->
+ Sz = 1024,
+ huge_iolist(list_to_binary(mk_list(Sz)), Sz, Lim).
+
+huge_iolist(X, Sz, Lim) when Sz >= Lim ->
+ X;
+huge_iolist(X, Sz, Lim) ->
+ huge_iolist([X, X], Sz*2, Lim).
+
+cmp_node(Node, {M, F, A}) ->
+ Res = rpc:call(Node, M, F, A),
+ Res = apply(M, F, A),
+ ok.
+
make_sub_binary(Bin) when is_binary(Bin) ->
{_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3),
B;
@@ -1467,3 +1658,78 @@ get_reds() ->
erts_debug:set_internal_state(available_internal_state, true),
get_reds()
end.
+
+-define(LARGE_BIN, (512*1024+10)).
+-define(LARGE_BIN_LIM, (1024*1024)).
+
+mk_list(0, Acc) ->
+ Acc;
+mk_list(Sz, Acc) ->
+ mk_list(Sz-1, [$A+(Sz band 63) | Acc]).
+
+mk_list(Sz) when Sz >= ?LARGE_BIN_LIM ->
+ SzLeft = Sz - ?LARGE_BIN,
+ SzHd = SzLeft div 2,
+ SzTl = SzLeft - SzHd,
+ [mk_list(SzHd, []), erlang:list_to_binary(mk_list(?LARGE_BIN, [])), mk_list(SzTl, [])];
+mk_list(Sz) ->
+ mk_list(Sz, []).
+
+mk_list_lb(Sz) when Sz >= ?LARGE_BIN_LIM ->
+ SzLeft = Sz - ?LARGE_BIN,
+ SzHd = SzLeft div 2,
+ SzTl = SzLeft - SzHd,
+ [mk_list(SzHd, []), erlang:list_to_binary(mk_list(?LARGE_BIN, [])), mk_list(SzTl, [])];
+mk_list_lb(Sz) ->
+ mk_list(Sz, []).
+
+
+list2iolist(List) ->
+ list2iolist(List, []).
+
+list2iolist([], Acc) ->
+ Acc;
+list2iolist([X0, X1, X2, X3, X4, X5 | Xs], Acc) when is_integer(X0), 0 =< X0, X0 < 256,
+ is_integer(X1), 0 =< X1, X1 < 256,
+ is_integer(X2), 0 =< X2, X2 < 256,
+ is_integer(X3), 0 =< X3, X3 < 256,
+ is_integer(X4), 0 =< X4, X4 < 256,
+ is_integer(X5), 0 =< X5, X5 < 256 ->
+ NewAcc = case (X0+X1+X2+X3+X4+X5) band 3 of
+ 0 ->
+ [Acc, [[[[[[[[[[[[X0,[],<<"">>,X1]]]]]]]]],[X2,X3]],[],[],[],[],X4],X5]];
+ 1 ->
+ [Acc, [], erlang:list_to_binary([X0, X1, X2, X3, X4, X5])];
+ 2 ->
+ [Acc, [[[[X0|erlang:list_to_binary([X1])],[X2|erlang:list_to_binary([X3])],[X4|erlang:list_to_binary([X5])]]]|<<"">>]];
+ 3 ->
+ [Acc, X0, X1, X2, <<"">>, [], X3, X4 | erlang:list_to_binary([X5])]
+ end,
+ list2iolist(Xs, NewAcc);
+list2iolist([X | Xs], Acc) ->
+ list2iolist(Xs, [Acc,X]).
+
+list2bitstrlist(List) ->
+ [list2bitstrlist(List, []), <<4:7>>].
+
+list2bitstrlist([], Acc) ->
+ Acc;
+list2bitstrlist([X0, X1, X2, X3, X4, X5 | Xs], Acc) when is_integer(X0), 0 =< X0, X0 < 256,
+ is_integer(X1), 0 =< X1, X1 < 256,
+ is_integer(X2), 0 =< X2, X2 < 256,
+ is_integer(X3), 0 =< X3, X3 < 256,
+ is_integer(X4), 0 =< X4, X4 < 256,
+ is_integer(X5), 0 =< X5, X5 < 256 ->
+ NewAcc = case (X0+X1+X2+X3+X4+X5) band 3 of
+ 0 ->
+ [Acc, [[[[[[[[[[[[X0,[],<<"">>,X1]]]]]]]]],[X2,X3]],[],[],[],[],X4],X5]];
+ 1 ->
+ [Acc, [], <<X0:X1>>, <<X2:X3>>, <<X4:X5>>];
+ 2 ->
+ [Acc, [[[[X0|<<X1:X2>>],X3]],[X4|erlang:list_to_binary([X5])]|<<"">>]];
+ 3 ->
+ [Acc, X0, X1, X2, <<"">>, [], X3, X4 | erlang:list_to_binary([X5])]
+ end,
+ list2bitstrlist(Xs, NewAcc);
+list2bitstrlist([X | Xs], Acc) ->
+ list2bitstrlist(Xs, [Acc,X]).
diff --git a/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c b/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c
index a1299fe807..6b9d4745ba 100644
--- a/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/smaller_major_vsn_drv.c
@@ -20,12 +20,12 @@
* Author: Rickard Green
*
* Description: Implementation of a driver with a smaller major
- * driver version than the current system.
+ * driver version than allowed on load.
*/
#define VSN_MISMATCH_DRV_NAME_STR "smaller_major_vsn_drv"
#define VSN_MISMATCH_DRV_NAME smaller_major_vsn_drv
-#define VSN_MISMATCH_DRV_MAJOR_VSN_DIFF (-1)
+#define VSN_MISMATCH_DRV_MAJOR_VSN_DIFF (ERL_DRV_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD - ERL_DRV_EXTENDED_MAJOR_VERSION - 1)
#define VSN_MISMATCH_DRV_MINOR_VSN_DIFF 0
#include "vsn_mismatch_drv_impl.c"
diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl
index 87778dd0c2..e5c904cfb9 100644
--- a/erts/emulator/test/erts_debug_SUITE.erl
+++ b/erts/emulator/test/erts_debug_SUITE.erl
@@ -67,6 +67,9 @@ test_size(Config) when is_list(Config) ->
2 = do_test_size({[]}),
3 = do_test_size({a,b}),
7 = do_test_size({a,[b,c]}),
+ 8 = do_test_size(#{b => 2,c => 3}),
+ 4 = do_test_size(#{}),
+ 32 = do_test_size(#{b => 2,c => 3,txt => "hello world"}),
%% Test internal consistency of sizes, but without testing
%% exact sizes.
@@ -97,6 +100,9 @@ test_size(Config) when is_list(Config) ->
do_test_size({SimplestFun,SimplestFun},
2*FunSz0+do_test_size({a,b}),
FunSz0+do_test_size({a,b})),
+
+ M = id(#{ "atom" => first, i => 0}),
+ do_test_size([M,M#{ "atom" := other },M#{i := 42}],54,32),
ok.
do_test_size(Term) ->
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 8cc5621181..888ed8e272 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -29,6 +29,7 @@
t_guard_receive/1, t_guard_fun/1,
t_list_comprehension/1,
t_map_sort_literals/1,
+ t_map_equal/1,
%t_size/1,
t_map_size/1,
@@ -74,6 +75,7 @@ all() -> [
t_update_assoc,t_update_exact,
t_guard_bifs, t_guard_sequence, t_guard_update,
t_guard_receive,t_guard_fun, t_list_comprehension,
+ t_map_equal,
t_map_sort_literals,
%% Specific Map BIFs
@@ -450,6 +452,23 @@ t_map_sort_literals(Config) when is_list(Config) ->
ok.
+t_map_equal(Config) when is_list(Config) ->
+ true = id(#{}) =:= id(#{}),
+ false = id(#{}) =:= id(#{a=>1}),
+ false = id(#{a=>1}) =:= id(#{}),
+ true = id(#{ "a" => "hi", b => 134 }) =:= id(#{ b => 134,"a" => "hi"}),
+
+ false = id(#{ a => 1 }) =:= id(#{ a => 2}),
+ false = id(#{ a => 2 }) =:= id(#{ a => 1}),
+ false = id(#{ a => 2, b => 1 }) =:= id(#{ a => 1, b => 3}),
+ false = id(#{ a => 1, b => 1 }) =:= id(#{ a => 1, b => 3}),
+
+ true = id(#{ a => 1 }) =:= id(#{ a => 1}),
+ true = id(#{ "a" => 2 }) =:= id(#{ "a" => 2}),
+ true = id(#{ "a" => 2, b => 3 }) =:= id(#{ "a" => 2, b => 3}),
+ true = id(#{ a => 1, b => 3, c => <<"wat">> }) =:= id(#{ a => 1, b => 3, c=><<"wat">>}),
+ ok.
+
%% BIFs
t_bif_map_get(Config) when is_list(Config) ->
@@ -794,16 +813,16 @@ t_map_encode_decode(Config) when is_list(Config) ->
%% literally #{ b=>2, a=>1 } in the internal order
#{ a:=1, b:=2 } =
- erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,2,97,1>>),
+ erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,97,2,100,0,1,97,97,1>>),
%% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order
#{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3,
107,0,2,104,105, % "hi" :: list()
- 100,0,1,97, % a :: atom()
- 100,0,1,98, % b :: atom()
107,0,5,118,97,108,117,101, % "value" :: list()
+ 100,0,1,97, % a :: atom()
97,33, % 33 :: integer()
+ 100,0,1,98, % b :: atom()
97,55 % 55 :: integer()
>>),
@@ -815,11 +834,17 @@ t_map_encode_decode(Config) when is_list(Config) ->
%% uniqueness violation
%% literally #{ a=>1, "hi"=>"value", a=>2 }
{'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
- erlang:binary_to_term(<<131,116,0,0,0,3,100,0,1,97,107,0,2,104,105,100,0,1,97,97,1,107,0,5,118,97,108,117,101,97,2>>)),
+ erlang:binary_to_term(<<131,116,0,0,0,3,
+ 100,0,1,97,
+ 97,1,
+ 107,0,2,104,105,
+ 107,0,5,118,97,108,117,101,
+ 100,0,1,97,
+ 97,2>>)),
%% bad size (too large)
{'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
- erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,100,0,1,98,97,1,97,1>>)),
+ erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,97,1,100,0,1,98,97,1>>)),
%% bad size (too small) .. should fail just truncate it .. weird.
%% possibly change external format so truncated will be #{a:=1}
@@ -833,7 +858,8 @@ map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) ->
B0 = erlang:term_to_binary(M1),
Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]),
%% sort Ks and Vs according to term spec, then match it
- ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]),
+ KVbins = lists:foldr(fun({_,Kbin,Vbin}, Acc) -> [Kbin,Vbin | Acc] end, [], Ls),
+ ok = match_encoded_map(B0, length(Ls), KVbins),
%% decode and match it
M1 = erlang:binary_to_term(B0),
map_encode_decode_and_match(Pairs,Ls,M1);
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index 330bef7104..fdce157abc 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -42,7 +42,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:seconds(10)),
+ Dog=?t:timetrap(?t:seconds(30)),
[{watchdog, Dog}|Config].
end_per_testcase(_Func, Config) ->
@@ -1009,12 +1009,14 @@ loop_runner(Collector, Fun, Laps) ->
end,
loop_runner_cont(Collector, Fun, 0, Laps).
-loop_runner_cont(_Collector, _Fun, Laps, Laps) ->
+loop_runner_cont(Collector, _Fun, Laps, Laps) ->
receive
- {done, Collector} ->
- io:format("loop_runner ~p exit after ~p laps\n", [self(), Laps]),
- Collector ! {gone, self()}
- end;
+ {done, Collector} -> ok;
+ {abort, Collector} -> ok
+ end,
+ io:format("loop_runner ~p exit after ~p laps\n", [self(), Laps]),
+ Collector ! {gone, self()};
+
loop_runner_cont(Collector, Fun, N, Laps) ->
Fun(),
receive
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index a854d3f05b..b2da6f58af 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -37,7 +37,7 @@
threading/1, send/1, send2/1, send3/1, send_threaded/1, neg/1,
is_checks/1,
get_length/1, make_atom/1, make_string/1, reverse_list_test/1,
- otp_9668/1, consume_timeslice/1, dirty_nif/1
+ otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1
]).
-export([many_args_100/100]).
@@ -64,7 +64,7 @@ all() ->
resource_takeover, threading, send, send2, send3,
send_threaded, neg, is_checks, get_length, make_atom,
make_string,reverse_list_test,
- otp_9668, consume_timeslice, dirty_nif
+ otp_9668, consume_timeslice, dirty_nif, dirty_nif_send
].
groups() ->
@@ -1538,6 +1538,24 @@ dirty_nif(Config) when is_list(Config) ->
{skipped,"No dirty scheduler support"}
end.
+dirty_nif_send(Config) when is_list(Config) ->
+ try erlang:system_info(dirty_cpu_schedulers) of
+ N when is_integer(N) ->
+ ensure_lib_loaded(Config),
+ Parent = self(),
+ Pid = spawn_link(fun() ->
+ Self = self(),
+ {ok, Self} = receive_any(),
+ Parent ! {ok, Self}
+ end),
+ {ok, Pid} = send_from_dirty_nif(Pid),
+ {ok, Pid} = receive_any(),
+ ok
+ catch
+ error:badarg ->
+ {skipped,"No dirty scheduler support"}
+ end.
+
next_msg(_Pid) ->
receive
M -> M
@@ -1668,6 +1686,7 @@ type_sizes() -> ?nif_stub.
otp_9668_nif(_) -> ?nif_stub.
consume_timeslice_nif(_,_) -> ?nif_stub.
call_dirty_nif(_,_,_) -> ?nif_stub.
+send_from_dirty_nif(_) -> ?nif_stub.
%% maps
is_map_nif(_) -> ?nif_stub.
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 160f4843ad..955dc64189 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1533,6 +1533,37 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
return dirty_nif(env, argc, argv);
}
}
+
+static ERL_NIF_TERM dirty_sender(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM result;
+ ErlNifPid pid;
+ ErlNifEnv* menv;
+ int res;
+
+ enif_get_local_pid(env, argv[0], &pid);
+ 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);
+ if (!res)
+ /* Note the next line will crash, since dirty nifs can't return exceptions.
+ * This is intentional, since enif_send should not fail if the test succeeds.
+ */
+ return enif_schedule_dirty_nif_finalizer(env, enif_make_badarg(env), enif_dirty_nif_finalizer);
+ else
+ return enif_schedule_dirty_nif_finalizer(env, result, enif_dirty_nif_finalizer);
+}
+
+static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM result;
+ ErlNifPid pid;
+
+ if (!enif_get_local_pid(env, argv[0], &pid))
+ return enif_make_badarg(env);
+ return enif_schedule_dirty_nif(env, ERL_NIF_DIRTY_JOB_CPU_BOUND, dirty_sender, argc, argv);
+}
#endif
static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -1713,6 +1744,7 @@ static ErlNifFunc nif_funcs[] =
{"consume_timeslice_nif", 2, consume_timeslice_nif},
#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
{"call_dirty_nif", 3, call_dirty_nif},
+ {"send_from_dirty_nif", 1, send_from_dirty_nif},
#endif
{"is_map_nif", 1, is_map_nif},
{"get_map_size_nif", 1, get_map_size_nif},
diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c
index 55a0d2ac4f..11b5d0cc35 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_mod.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c
@@ -217,7 +217,8 @@ static int upgrade(ErlNifEnv* env, void** priv, void** old_priv_data, ERL_NIF_TE
*priv = *old_priv_data;
do_load_info(env, load_info, &retval);
-
+ if (retval)
+ NifModPrivData_release(data);
return retval;
}
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index 202a8b7537..e01b2f253b 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1698,12 +1698,13 @@ otp_5119(Config) when is_list(Config) ->
Path = ?config(data_dir, Config),
ok = load_driver(Path, "exit_drv"),
PI1 = port_ix(otp_5119_fill_empty_port_tab([])),
- PI2 = port_ix(erlang:open_port({spawn, "exit_drv"}, [])),
+ Port2 = erlang:open_port({spawn, "exit_drv"}, []),
+ PI2 = port_ix(Port2),
{PortIx1, PortIx2} = case PI2 > PI1 of
true ->
{PI1, PI2};
false ->
- {port_ix(otp_5119_fill_empty_port_tab([PI2])),
+ {port_ix(otp_5119_fill_empty_port_tab([Port2])),
port_ix(erlang:open_port({spawn, "exit_drv"}, []))}
end,
MaxPorts = max_ports(),
@@ -2318,7 +2319,7 @@ close_deaf_port(Config) when is_list(Config) ->
test_server:timetrap_cancel(Dog),
Res.
-close_deaf_port_1(1000, _) ->
+close_deaf_port_1(200, _) ->
ok;
close_deaf_port_1(N, Cmd) ->
Timeout = integer_to_list(random:uniform(5*1000)),
diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl
index ceb4afb5cf..f959714be7 100644
--- a/erts/emulator/test/system_info_SUITE.erl
+++ b/erts/emulator/test/system_info_SUITE.erl
@@ -155,6 +155,7 @@ misc_smoke_tests(Config) when is_list(Config) ->
?line true = is_binary(erlang:system_info(loaded)),
?line true = is_binary(erlang:system_info(dist)),
?line ok = try erlang:system_info({cpu_topology,erts_get_cpu_topology_error_case}), fail catch error:badarg -> ok end,
+ true = lists:member(erlang:system_info(tolerant_timeofday), [enabled, disabled]),
?line ok.
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index 2251575e5a..4d7598cf1f 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -181,6 +181,13 @@ send_trace(Config) when is_list(Config) ->
?line {trace, Sender, send, to_receiver, Receiver} = receive_first(),
?line receive_nothing(),
+ %% Check that a message sent to another registered process is traced.
+ register(?MODULE,Receiver),
+ Sender ! {send_please, ?MODULE, to_receiver},
+ {trace, Sender, send, to_receiver, ?MODULE} = receive_first(),
+ receive_nothing(),
+ unregister(?MODULE),
+
%% Check that a message sent to this process is traced.
?line Sender ! {send_please, self(), to_myself},
?line receive to_myself -> ok end,
@@ -188,6 +195,21 @@ send_trace(Config) when is_list(Config) ->
?line {trace, Sender, send, to_myself, Self} = receive_first(),
?line receive_nothing(),
+ %% Check that a message sent to dead process is traced.
+ {Pid,Ref} = spawn_monitor(fun() -> ok end),
+ receive {'DOWN',Ref,_,_,_} -> ok end,
+ Sender ! {send_please, Pid, to_dead},
+ {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first(),
+ receive_nothing(),
+
+ %% Check that a message sent to unknown registrated process is traced.
+ BadargSender = fun_spawn(fun sender/0),
+ 1 = erlang:trace(BadargSender, true, [send]),
+ unlink(BadargSender),
+ BadargSender ! {send_please, not_registered, to_unknown},
+ {trace, BadargSender, send, to_unknown, not_registered} = receive_first(),
+ receive_nothing(),
+
%% Another process should not be able to trace Sender.
?line Intruder = fun_spawn(fun() -> erlang:trace(Sender, true, [send]) end),
?line {'EXIT', Intruder, {badarg, _}} = receive_first(),
diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl
index 5dfa87bbee..3036d2957b 100644
--- a/erts/emulator/test/trace_call_time_SUITE.erl
+++ b/erts/emulator/test/trace_call_time_SUITE.erl
@@ -33,7 +33,7 @@
%% Exported end user tests
-export([seq/3, seq_r/3]).
--export([loaded/1, a_function/1, a_called_function/1, dec/1, nif_dec/1]).
+-export([loaded/1, a_function/1, a_called_function/1, dec/1, nif_dec/1, dead_tracer/1]).
-define(US_ERROR, 10000).
-define(R_ERROR, 0.8).
@@ -89,7 +89,7 @@ all() ->
true -> [not_run];
false ->
[basic, on_and_off, info, pause_and_restart, scheduling,
- combo, bif, nif, called_function]
+ combo, bif, nif, called_function, dead_tracer]
end.
groups() ->
@@ -470,6 +470,92 @@ called_function(Config) when is_list(Config) ->
?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]),
ok.
+%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+dead_tracer(Config) when is_list(Config) ->
+ Self = self(),
+ FirstTracer = tracer(),
+ StartTracing = fun() -> turn_on_tracing(Self) end,
+ tell_tracer(FirstTracer, StartTracing),
+ [1,2,3,4,5,6,7,8] = seq(1, 8, fun(I) -> I + 1 end),
+ Ref = erlang:monitor(process, FirstTracer),
+ FirstTracer ! quit,
+ receive
+ {'DOWN',Ref,process,FirstTracer,normal} ->
+ ok
+ end,
+ erlang:yield(),
+
+ %% Collect and check that we only get call_time info for the current process.
+ Info1 = collect_all_info(),
+ [] = other_than_self(Info1),
+ io:format("~p\n", [Info1]),
+
+ %% Note that we have not turned off tracing for the current process,
+ %% but that the tracer has terminated. No more call_time information should be recorded.
+ [1,2,3] = seq(1, 3, fun(I) -> I + 1 end),
+ [] = collect_all_info(),
+
+ %% When we start a second tracer process, that tracer process must
+ %% not inherit the tracing flags and the dead tracer (even though
+ %% we used set_on_spawn).
+ SecondTracer = tracer(),
+ tell_tracer(SecondTracer, StartTracing),
+ Seq20 = lists:seq(1, 20),
+ Seq20 = seq(1, 20, fun(I) -> I + 1 end),
+ Info2 = collect_all_info(),
+ io:format("~p\n", [Info2]),
+ [] = other_than_self(Info2),
+ SecondTracer ! quit,
+
+ ok.
+
+other_than_self(Info) ->
+ [{Pid,MFA} || {MFA,[{Pid,_,_,_}]} <- Info,
+ Pid =/= self()].
+
+tell_tracer(Tracer, Fun) ->
+ Tracer ! {execute,self(),Fun},
+ receive
+ {Tracer,executed} ->
+ ok
+ end.
+
+tracer() ->
+ spawn_link(fun Loop() ->
+ receive
+ quit ->
+ ok;
+ {execute,From,Fun} ->
+ Fun(),
+ From ! {self(),executed},
+ Loop()
+ end
+ end).
+
+turn_on_tracing(Pid) ->
+ _ = erlang:trace(Pid, true, [call,set_on_spawn]),
+ _ = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_time]),
+ _ = now(),
+ ok.
+
+collect_all_info() ->
+ collect_all_info([{?MODULE,F,A} || {F,A} <- module_info(functions)] ++
+ erlang:system_info(snifs)).
+
+collect_all_info([MFA|T]) ->
+ CallTime = erlang:trace_info(MFA, call_time),
+ erlang:trace_pattern(MFA, restart, [call_time]),
+ case CallTime of
+ {call_time,false} ->
+ collect_all_info(T);
+ {call_time,[]} ->
+ collect_all_info(T);
+ {call_time,[_|_]=List} ->
+ [{MFA,List}|collect_all_info(T)]
+ end;
+collect_all_info([]) -> [].
+
%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% The Tests
%%%
@@ -478,7 +564,6 @@ called_function(Config) when is_list(Config) ->
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Local helpers
-
load_nif(Config) ->
?line Path = ?config(data_dir, Config),
?line ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0).
@@ -602,8 +687,11 @@ collect(A, Ref) ->
end.
setup() ->
+ setup([]).
+
+setup(Opts) ->
Pid = spawn_link(fun() -> loop() end),
- ?line 1 = erlang:trace(Pid, true, [call]),
+ 1 = erlang:trace(Pid, true, [call|Opts]),
Pid.
execute(Pids, Mfa) when is_list(Pids) ->
diff --git a/erts/emulator/utils/gen_git_version b/erts/emulator/utils/gen_git_version
index ef06a4b8e2..9faf015b62 100755
--- a/erts/emulator/utils/gen_git_version
+++ b/erts/emulator/utils/gen_git_version
@@ -5,9 +5,9 @@ OUTPUT_FILE=$1
if command -v git 2>&1 >/dev/null &&
test -d $ERL_TOP/.git -o -f $ERL_TOP/.git
then
- VSN=`git describe --match "OTP_R[0-9][0-9][A-B]*" HEAD`
+ VSN=`git describe --match "OTP-[0-9]*" HEAD`
case "$VSN" in
- OTP_R*-g*)
+ OTP-*-g*)
VSN=`echo $VSN | sed -e 's/.*-g\\(.*\\)/\\1/g'` ;;
*) VSN="na" ;;
esac
@@ -36,4 +36,4 @@ then
fi
exit 0
fi
-exit 1 \ No newline at end of file
+exit 1
diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in
index 2ea8630491..0c7787a3b1 100644
--- a/erts/epmd/src/Makefile.in
+++ b/erts/epmd/src/Makefile.in
@@ -84,7 +84,7 @@ LD = @LD@
ifeq ($(findstring ose,$(TARGET)),ose)
LIBS = $(ERTS_INTERNAL_LIBS) @LIBS@
else
-LIBS = @LIBS@ $(ERTS_INTERNAL_LIBS)
+LIBS = @LIBS@ @SYSTEMD_DAEMON_LIBS@ $(ERTS_INTERNAL_LIBS)
endif
LDFLAGS = @LDFLAGS@
@@ -148,7 +148,7 @@ endif
ifeq ($(findstring ose,$(TARGET)),ose)
$(BINDIR)/$(EPMD): $(EPMD_OBJS) $(ERTS_LIB) $(OSE_LM_OBJS)
- $(call build-ose-load-module, $@, $(EPMD_OBJS) $(OSE_LM_OBJS), $(LIBS), $(LMCONF))
+ $(call build-ose-load-module, $@, $(EPMD_OBJS) $(OSE_LM_OBJS), $(LIBS), $(EPMD_LMCONF))
else
$(BINDIR)/$(EPMD): $(EPMD_OBJS) $(ERTS_LIB)
$(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(EPMD_OBJS) $(LIBS)
diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c
index 5d5c3a1c3c..3cfa7a782f 100644
--- a/erts/epmd/src/epmd.c
+++ b/erts/epmd/src/epmd.c
@@ -52,7 +52,7 @@ static int epmd_main(int, char **, int);
int epmd_dbg(int level,int port) /* Utility to debug epmd... */
{
- char* argv[MAX_DEBUG+2];
+ char* argv[MAX_DEBUG+4];
char ibuff[100];
int argc = 0;
@@ -175,6 +175,9 @@ int main(int argc, char** argv)
g->nodes.reg = g->nodes.unreg = g->nodes.unreg_tail = NULL;
g->nodes.unreg_count = 0;
g->active_conn = 0;
+#ifdef HAVE_SYSTEMD_SD_DAEMON_H
+ g->is_systemd = 0;
+#endif
for (i = 0; i < MAX_LISTEN_SOCKETS; i++)
g->listenfd[i] = -1;
@@ -248,8 +251,12 @@ int main(int argc, char** argv)
else
usage(g);
epmd_cleanup_exit(g,0);
- }
- else
+#ifdef HAVE_SYSTEMD_SD_DAEMON_H
+ } else if (strcmp(argv[0], "-systemd") == 0) {
+ g->is_systemd = 1;
+ argv++; argc--;
+#endif
+ } else
usage(g);
}
dbg_printf(g,1,"epmd running - daemon = %d",g->is_daemon);
@@ -454,6 +461,11 @@ static void usage(EpmdVars *g)
fprintf(stderr, " Forcibly unregisters a name with epmd\n");
fprintf(stderr, " (only allowed if -relaxed_command_check was given when \n");
fprintf(stderr, " epmd was started).\n");
+#ifdef HAVE_SYSTEMD_SD_DAEMON_H
+ fprintf(stderr, " -systemd\n");
+ fprintf(stderr, " Wait for socket from systemd. The option makes sense\n");
+ fprintf(stderr, " when started from .socket unit.\n");
+#endif
epmd_cleanup_exit(g,1);
}
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
index d4597be30c..c8f2192f7f 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
@@ -125,6 +125,9 @@
# include "sys/select.h"
#endif
+#ifdef HAVE_SYSTEMD_SD_DAEMON_H
+# include <systemd/sd-daemon.h>
+#endif
/* ************************************************************************ */
/* Replace some functions by others by making the function name a macro */
@@ -337,6 +340,9 @@ typedef struct {
int listenfd[MAX_LISTEN_SOCKETS];
char *addresses;
char **argv;
+#ifdef HAVE_SYSTEMD_SD_DAEMON_H
+ int is_systemd;
+#endif
} EpmdVars;
void dbg_printf(EpmdVars*,int,const char*,...);
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
index 247fd34d5a..48fd7a5f9c 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -213,6 +213,39 @@ void run(EpmdVars *g)
node_init(g);
g->conn = conn_init(g);
+#ifdef HAVE_SYSTEMD_DAEMON
+ if (g->is_systemd)
+ {
+ int n;
+
+ dbg_printf(g,2,"try to obtain sockets from systemd");
+
+ n = sd_listen_fds(0);
+ if (n < 0)
+ {
+ dbg_perror(g,"cannot obtain sockets from systemd");
+ epmd_cleanup_exit(g,1);
+ }
+ else if (n == 0)
+ {
+ dbg_tty_printf(g,0,"systemd provides no sockets");
+ epmd_cleanup_exit(g,1);
+ }
+ else if (n > MAX_LISTEN_SOCKETS)
+ {
+ dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses", MAX_LISTEN_SOCKETS);
+ epmd_cleanup_exit(g,1);
+ }
+ num_sockets = n;
+ for (i = 0; i < num_sockets; i++)
+ {
+ g->listenfd[i] = listensock[i] = SD_LISTEN_FDS_START + i;
+ }
+ }
+ else
+ {
+#endif
+
dbg_printf(g,2,"try to initiate listening port %d", g->port);
if (g->addresses != NULL && /* String contains non-separator characters if: */
@@ -277,6 +310,9 @@ void run(EpmdVars *g)
SET_ADDR(iserv_addr[0],EPMD_ADDR_ANY,sport);
num_sockets = 1;
}
+#ifdef HAVE_SYSTEMD_DAEMON
+ }
+#endif
#if !defined(__WIN32__) && !defined(__OSE__)
/* We ignore the SIGPIPE signal that is raised when we call write
@@ -294,6 +330,13 @@ void run(EpmdVars *g)
FD_ZERO(&g->orig_read_mask);
g->select_fd_top = 0;
+#ifdef HAVE_SYSTEMD_SD_DAEMON_H
+ if (g->is_systemd)
+ for (i = 0; i < num_sockets; i++)
+ select_fd_set(g, listensock[i]);
+ else
+ {
+#endif
for (i = 0; i < num_sockets; i++)
{
if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0)
@@ -356,6 +399,9 @@ void run(EpmdVars *g)
}
select_fd_set(g, listensock[i]);
}
+#ifdef HAVE_SYSTEMD_SD_DAEMON_H
+ }
+#endif
dbg_tty_printf(g,2,"entering the main select() loop");
diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in
index 5c2cd8aded..0cf965f915 100644
--- a/erts/etc/common/Makefile.in
+++ b/erts/etc/common/Makefile.in
@@ -66,7 +66,9 @@ LIBS = @LIBS@
LDFLAGS = @LDFLAGS@
# For clock_gettime in heart
+ifneq ($(TARGET),arm-unknown-linux-androideabi)
RTLIBS = @LIBRT@
+endif
ifeq ($(TARGET),win32)
ifeq ($(TYPE),debug)
@@ -507,7 +509,7 @@ $(OBJDIR)/crt0_lm.o: $(CRT0_LM)
OSE_LM_OBJS += $(OBJDIR)/ose_confd.o $(OBJDIR)/crt0_lm.o
$(BINDIR)/run_erl_lm: $(OBJDIR)/run_erl_main.o $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(OBJDIR)/run_erl_common.o $(OBJDIR)/to_erl_common.o $(OSE_LM_OBJS)
- $(call build-ose-load-module, $@, $^, $(LIBS), $(LMCONF))
+ $(call build-ose-load-module, $@, $^, $(LIBS), $(RUN_ERL_LMCONF))
$(OBJDIR)/run_erl_main.o: $(OSEETC)/run_erl_main.c $(OSEETC)/run_erl.h ../common/to_erl_common.h $(RC_GENERATED)
diff --git a/erts/etc/common/to_erl_common.c b/erts/etc/common/to_erl_common.c
index a49be44b6c..ab706fffe0 100644
--- a/erts/etc/common/to_erl_common.c
+++ b/erts/etc/common/to_erl_common.c
@@ -126,7 +126,8 @@ static int version_handshake(char* buf, int len, int wfd);
#define READ_AIO(REQ,FD,SIZE,BUFF) \
SET_AIO(REQ,FD,SIZE,BUFF); \
if (aio_read(&(REQ)) != 0) \
- fprintf(stderr,"aio_read of child_read_req(%d) failed\n",FD)
+ fprintf(stderr,"aio_read of child_read_req(%d) failed" \
+ "with error %d\n",FD,errno)
union SIGNAL {
SIGSELECT signo;
diff --git a/erts/etc/ose/etc.lmconf b/erts/etc/ose/etc.lmconf
new file mode 100644
index 0000000000..b402b325b1
--- /dev/null
+++ b/erts/etc/ose/etc.lmconf
@@ -0,0 +1,20 @@
+OSE_LM_STACK_SIZES=256,512,1024,2048,4096,8192,16384,65536
+OSE_LM_SIGNAL_SIZES=31,63,127,255,1023,4095,16383,65535
+OSE_LM_POOL_SIZE=0x200000
+OSE_LM_MAIN_NAME=main
+OSE_LM_MAIN_STACK_SIZE=0xF000
+OSE_LM_MAIN_PRIORITY=20
+## Has to be of a type that allows MAM
+OSE_LM_PROGRAM_TYPE=APP_RAM
+OSE_LM_DATA_INIT=YES
+OSE_LM_BSS_INIT=YES
+OSE_LM_EXEC_MODEL=SHARED
+HEAP_MAX_SIZE=1000000000
+HEAP_SMALL_BUF_INIT_SIZE=64000000
+HEAP_LARGE_BUF_THRESHOLD=16000000
+HEAP_LOCK_TYPE=2
+
+# Setting the environment variable EFS_RESOLVE_TMO on the block to 0.
+# This will eliminiate delays when trying to open files on not mounted
+# volumes.
+EFS_RESOLVE_TMO=0
diff --git a/erts/etc/ose/run_erl_main.c b/erts/etc/ose/run_erl_main.c
index d396ebe93b..2d92924ff2 100644
--- a/erts/etc/ose/run_erl_main.c
+++ b/erts/etc/ose/run_erl_main.c
@@ -45,6 +45,8 @@ int main(int argc, char **argv)
char run_erl_usage[320],
to_erl_usage[120];
+ (void)stdin;(void)stdout;(void)stderr;
+
sprintf(run_erl_usage,RUN_ERL_USAGE,"run_erl [-daemon] [-block blockname]");
sprintf(to_erl_usage,TO_ERL_USAGE,"pipename");
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index 9e39764195..19c92681d0 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2012. All Rights Reserved.
+# Copyright Ericsson AB 2005-2014. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -54,13 +54,23 @@ document etp-help
% etp-mfa, etp-cp,
% etp-msgq, etpf-msgq,
% etp-stacktrace, etp-stackdump, etpf-stackdump, etp-dictdump
-% etp-offheapdump, etpf-offheapdump,
-% etp-print-procs, etp-search-heaps, etp-search-alloc,
+% etp-process-info, etp-process-memory-info
+% etp-port-info, etp-port-state, etp-port-sched-flags
+% etp-heapdump, etp-offheapdump, etpf-offheapdump,
+% etp-search-heaps, etp-search-alloc,
% etp-ets-tables, etp-ets-tabledump
%
% Complex commands that use the Erlang support module.
% etp-overlapped-heaps, etp-chart, etp-chart-start, etp-chart-end
-%
+%
+% System inspection
+% etp-system-info, etp-schedulers, etp-process, etp-ports, etp-lc-dump,
+% etp-migration-info, etp-processes-memory,
+% etp-compile-info, etp-config-h-info
+%
+% Platform specific (when gdb fails you)
+% etp-ppc-stacktrace
+%
% Erlang support module handling commands:
% etp-run
%
@@ -1055,7 +1065,9 @@ define etp-cp-1
set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2
end
if $etp_cp_p
- set $etp_cp_low = (Eterm**)($etp_cp_p->start + 8)
+ # 12 = MI_FUNCTIONS
+ set $etp_cp_low = (Eterm**)($etp_cp_p->start + 12)
+ # 0 = MI_NUM_FUNCTIONS
set $etp_cp_high = $etp_cp_low +$etp_cp_p->start[0]
set $etp_cp_p = 0
while $etp_cp_low < $etp_cp_high
@@ -3133,6 +3145,77 @@ document etp-ets-tabledump
%---------------------------------------------------------------------------
end
+define etp-lc-dump
+# Non-reentrant
+ set $etp_lc_dump_thread = erts_locked_locks
+ while $etp_lc_dump_thread
+ printf "Thread %s\n", $etp_lc_dump_thread->thread_name
+ set $etp_lc_dump_thread_locked = $etp_lc_dump_thread->locked.first
+ while $etp_lc_dump_thread_locked
+ if 0 <= $etp_lc_dump_thread_locked->id && $etp_lc_dump_thread_locked->id < sizeof(erts_lock_order)/sizeof(erts_lc_lock_order_t)
+ printf " %s:", erts_lock_order[$etp_lc_dump_thread_locked->id].name
+ else
+ printf " unkown:"
+ end
+ if ($etp_lc_dump_thread_locked->extra & 0x3) == 0x3
+ etp-1 $etp_lc_dump_thread_locked->extra
+ else
+ printf "%p", $etp_lc_dump_thread_locked->extra
+ end
+ if ($etp_lc_dump_thread_locked->flags & (0x1f)) == (1 << 0)
+ printf "[spinlock]"
+ end
+ if ($etp_lc_dump_thread_locked->flags & (0x1f)) == (1 << 1)
+ printf "[rw(spin)lock]"
+ end
+ if ($etp_lc_dump_thread_locked->flags & (0x1f)) == (1 << 2)
+ printf "[mutex]"
+ end
+ if ($etp_lc_dump_thread_locked->flags & (0x1f)) == (1 << 3)
+ printf "[rwmutex]"
+ end
+ if ($etp_lc_dump_thread_locked->flags & (0x1f)) == (1 << 4)
+ printf "[proclock]"
+ end
+ printf "(%s:%d)", $etp_lc_dump_thread_locked->file, $etp_lc_dump_thread_locked->line
+ if ($etp_lc_dump_thread_locked->flags & (0x60)) == (1 << 5)
+ printf "(r)"
+ end
+ if ($etp_lc_dump_thread_locked->flags & (0x60)) == ((1 << 5) | (1 << 6))
+ printf "(rw)"
+ end
+ printf "\n"
+ set $etp_lc_dump_thread_locked = $etp_lc_dump_thread_locked->next
+ end
+ set $etp_lc_dump_thread = $etp_lc_dump_thread->next
+ end
+end
+
+document etp-lc-dump
+%---------------------------------------------------------------------------
+% etp-lc-dump
+%
+% Dump all info about locks in the lock checker
+%---------------------------------------------------------------------------
+end
+
+define etp-ppc-stacktrace
+# Args: R1
+# Non-reentrant
+ set $etp_ppc_st_fp = ($arg0)
+ while $etp_ppc_st_fp
+ info symbol ((void**)$etp_ppc_st_fp)[1]
+ set $etp_ppc_st_fp = ((void**)$etp_ppc_st_fp)[0]
+ end
+end
+
+document etp-ppc-stacktrace
+%---------------------------------------------------------------------------
+% etp-ppc-stacktrace R1
+%
+% Dump stacktrace from given $r1 frame pointer
+%---------------------------------------------------------------------------
+end
############################################################################
# OSE support
@@ -3177,6 +3260,154 @@ define etp-thr
end
############################################################################
+# erl_alloc_util (blocks and carriers)
+#
+
+define etp-block-size-1
+#
+# In: (Block_t*) in $arg0
+# Out: Byte size in $etp_blk_sz
+#
+ if ($arg0)->bhdr & 1
+ # Free block
+ set $etp_blk_sz = ($arg0)->bhdr & ~7
+ else
+ # Allocated block
+ if !$etp_MBC_ABLK_SZ_MASK
+ if etp_arch_bits == 64
+ set $etp_MBC_ABLK_OFFSET_SHIFT = (64 - 24)
+ else
+ set $etp_MBC_ABLK_OFFSET_SHIFT = (32 - 9)
+ end
+ set $etp_MBC_ABLK_SZ_MASK = ((UWord)1 << $etp_MBC_ABLK_OFFSET_SHIFT) - 1 - 7
+ end
+ set $etp_blk_sz = ($arg0)->bhdr & $etp_MBC_ABLK_SZ_MASK
+ end
+end
+
+define etp-block2mbc-1
+#
+# In: (Block_t*) in $arg0
+# Out: (Carrier_t*) in $etp-mbc
+#
+ if (($arg0)->bhdr) & 1
+ # Free block
+ set $etp_mbc = ($arg0)->u.carrier
+ else
+ # Allocated block
+ if !$etp_MBC_ABLK_OFFSET_SHIFT
+ if etp_arch_bits == 64
+ set $etp_MBC_ABLK_OFFSET_SHIFT = (64 - 24)
+ else
+ set $etp_MBC_ABLK_OFFSET_SHIFT = (32 - 9)
+ end
+ end
+ set $etp_mbc = (Carrier_t*) ((((UWord)($arg0) >> 18) - (($arg0)->bhdr >> $etp_MBC_ABLK_OFFSET_SHIFT)) << 18)
+ end
+end
+
+define etp-block2mbc
+ etp-block2mbc-1 ((Block_t*)$arg0)
+ print $etp_mbc
+end
+
+document etp-block2mbc
+%---------------------------------------------------------------------------
+% Print pointer to multiblock carrier containing the argument (Block_t*)
+%---------------------------------------------------------------------------
+end
+
+define etp-block
+ etp-block-size-1 ((Block_t*)$arg0)
+ if ((Block_t*)$arg0)->bhdr & 1
+ printf "%#lx: FREE sz=%#x\n", ($arg0), $etp_blk_sz
+ else
+ printf "%#lx: ALLOCATED sz=%#x\n", ($arg0), $etp_blk_sz
+ end
+end
+
+document etp-block
+%---------------------------------------------------------------------------
+% Print memory block (Block_t*)
+%---------------------------------------------------------------------------
+end
+
+define etp-carrier-blocks
+ set $etp_crr = (Carrier_t*) $arg0
+ set $etp_alc = (Allctr_t*)($etp_crr->allctr.counter & ~7)
+ set $etp_blk = (Block_t*) ((char*)$etp_crr + $etp_alc->mbc_header_size)
+ set $etp_prev_blk = 0
+ set $etp_error_cnt = 0
+ set $etp_ablk_cnt = 0
+ set $etp_fblk_cnt = 0
+
+ if $argc == 2
+ set $etp_be_silent = $arg1
+ else
+ set $etp_be_silent = 0
+ end
+
+ while 1
+ if !$etp_be_silent
+ etp-block $etp_blk
+ else
+ etp-block-size-1 $etp_blk
+ end
+ etp-block2mbc-1 $etp_blk
+ if $etp_mbc != $etp_crr
+ printf "ERROR: Invalid carrier pointer %#lx in block at %#lx\n", $etp_mbc, $etp_blk
+ set $etp_error_cnt = $etp_error_cnt + 1
+ end
+ if $etp_prev_blk
+ if ($etp_prev_blk->bhdr & 1)
+ # Prev is FREE
+ if ($etp_blk->bhdr & 1)
+ printf "ERROR: Adjacent FREE blocks at %#lx and %#lx\n", $etp_prev_blk, $etp_blk
+ set $etp_error_cnt = $etp_error_cnt + 1
+ end
+ if !($etp_blk->bhdr & 2)
+ printf "ERROR: Missing PREV_FREE_BLK_HDR_FLG (2) in block at %#lx\n", $etp_blk
+ set $etp_error_cnt = $etp_error_cnt + 1
+ end
+ end
+ end
+ if $etp_blk->bhdr & 1
+ set $etp_fblk_cnt = $etp_fblk_cnt + 1
+ else
+ set $etp_ablk_cnt = $etp_ablk_cnt + 1
+ end
+ if $etp_blk->bhdr & 4
+ # Last block
+ loop_break
+ end
+ # All free blocks except the last have a footer
+ if ($etp_blk->bhdr & 1) && ((UWord*)((char*)$etp_blk + $etp_blk_sz))[-1] != $etp_blk_sz
+ printf "ERROR: Invalid footer of free block at %#lx\n", $etp_blk
+ end
+ set $etp_prev_blk = $etp_blk
+ set $etp_blk = (Block_t*) ((char*)$etp_blk + $etp_blk_sz)
+ end
+
+ if ((char*)$etp_blk + $etp_blk_sz) != ((char*)$etp_crr + ($etp_crr->chdr & ~7))
+ printf "ERROR: Last block not at end of carrier\n"
+ set $etp_error_cnt = $etp_error_cnt + 1
+ end
+ printf "Allocated blocks: %u\n", $etp_ablk_cnt
+ printf "Free blocks: %u\n", $etp_fblk_cnt
+ if $etp_error_cnt
+ printf "%u ERRORs reported above\n", $etp-error-cnt
+ end
+end
+
+document etp-carrier-blocks
+%---------------------------------------------------------------------------
+% Check and (maybe) print all memory blocks in carrier
+% Args: (Carrier_t*) [1=be_silent]
+%---------------------------------------------------------------------------
+end
+
+
+############################################################################
# Toolbox parameter handling
#
diff --git a/erts/etc/win32/nsis/erlang.nsi b/erts/etc/win32/nsis/erlang.nsi
index 162e634148..f4fd2b4cdb 100644
--- a/erts/etc/win32/nsis/erlang.nsi
+++ b/erts/etc/win32/nsis/erlang.nsi
@@ -93,7 +93,6 @@ SectionIn 1 RO
skip_silent_mode:
SetOutPath "$INSTDIR"
- File "${TESTROOT}\OTP_VERSION"
File "${TESTROOT}\Install.ini"
File "${TESTROOT}\Install.exe"
File /r "${TESTROOT}\releases"
diff --git a/erts/etc/win32/nsis/erlang20.nsi b/erts/etc/win32/nsis/erlang20.nsi
index 3ee33e8121..3333c4a9aa 100644
--- a/erts/etc/win32/nsis/erlang20.nsi
+++ b/erts/etc/win32/nsis/erlang20.nsi
@@ -144,7 +144,6 @@ Section "Development" SecErlangDev
SectionIn 1 RO
SetOutPath "$INSTDIR"
- File "${TESTROOT}\OTP_VERSION"
File "${TESTROOT}\Install.ini"
File "${TESTROOT}\Install.exe"
SetOutPath "$INSTDIR\releases"
diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h
index ee861065c5..6c931e0cd4 100644
--- a/erts/include/internal/ethr_mutex.h
+++ b/erts/include/internal/ethr_mutex.h
@@ -354,7 +354,7 @@ void ethr_rwmutex_rwunlock(ethr_rwmutex *);
#ifdef ETHR_MTX_HARD_DEBUG
#define ETHR_MTX_HARD_ASSERT(A) \
- ((void) ((A) ? 1 : ethr_assert_failed(__FILE__, __LINE__, #A)))
+ ((void) ((A) ? 1 : ethr_assert_failed(__FILE__, __LINE__, __func__,#A)))
#else
#define ETHR_MTX_HARD_ASSERT(A) ((void) 1)
#endif
diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h
index 230388cce5..70e9c937e9 100644
--- a/erts/include/internal/ethread.h
+++ b/erts/include/internal/ethread.h
@@ -403,6 +403,18 @@ extern ethr_runtime_t ethr_runtime__;
#include "ethr_atomics.h" /* The atomics API */
+#if defined (ETHR_OSE_THREADS)
+static ETHR_INLINE void
+ose_yield(void)
+{
+ if (get_ptype(current_process()) == OS_PRI_PROC) {
+ set_pri(get_pri(current_process()));
+ } else {
+ delay(1);
+ }
+}
+#endif
+
#if defined(__GNUC__) && !defined(ETHR_OSE_THREADS)
# ifndef ETHR_SPIN_BODY
# if defined(__i386__) || defined(__x86_64__)
@@ -421,9 +433,9 @@ extern ethr_runtime_t ethr_runtime__;
# endif
#elif defined(ETHR_OSE_THREADS)
# ifndef ETHR_SPIN_BODY
-# define ETHR_SPIN_BODY set_pri(get_pri(current_process()))
+# define ETHR_SPIN_BODY ose_yield()
# else
-# error "OSE should use set_pri(get_pri(current_process()))"
+# error "OSE should use ose_yield()"
# endif
#endif
@@ -456,7 +468,7 @@ extern ethr_runtime_t ethr_runtime__;
# define ETHR_YIELD() (pthread_yield(), 0)
# endif
# elif defined(ETHR_OSE_THREADS)
-# define ETHR_YIELD() (set_pri(get_pri(current_process())), 0)
+# define ETHR_YIELD() (ose_yield(), 0)
# else
# define ETHR_YIELD() (ethr_compiler_barrier(), 0)
# endif
diff --git a/erts/lib_src/pthread/ethread.c b/erts/lib_src/pthread/ethread.c
index 7f27b5f29c..79784c5b84 100644
--- a/erts/lib_src/pthread/ethread.c
+++ b/erts/lib_src/pthread/ethread.c
@@ -541,7 +541,11 @@ int ethr_sigmask(int how, const sigset_t *set, sigset_t *oset)
return EINVAL;
}
#endif
- return pthread_sigmask(how, set, oset);
+#if defined(__ANDROID__)
+ return sigprocmask(how, set, oset);
+#else
+ return pthread_sigmask(how, set, oset);
+#endif
}
int ethr_sigwait(const sigset_t *set, int *sig)
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 26f851fd7a..f1e588320b 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/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 344176b71b..fdc7401475 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index bd402f9a98..ba45e4e011 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 5d49eed469..26f779500c 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 2607739d12..4d22d8bace 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 d665d35ab1..efc8347b6e 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 201252de34..6c49b5185e 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 ff8265414e..fe5431c5ff 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 f80d4a96e5..73be297bbb 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 dba3ec9546..193cebdc31 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index fbc37bd955..4ff0513321 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -79,7 +79,7 @@
-export([binary_to_integer/1,binary_to_integer/2]).
-export([binary_to_list/1]).
-export([binary_to_list/3, binary_to_term/1, binary_to_term/2]).
--export([bit_size/1, bitsize/1, bitstr_to_list/1, bitstring_to_list/1]).
+-export([bit_size/1, bitsize/1, bitstring_to_list/1]).
-export([bump_reductions/1, byte_size/1, call_on_load_function/1]).
-export([cancel_timer/1, check_old_code/1, check_process_code/2,
check_process_code/3, crc32/1]).
@@ -100,7 +100,7 @@
-export([integer_to_binary/1, integer_to_list/1]).
-export([iolist_size/1, iolist_to_binary/1]).
-export([is_alive/0, is_builtin/3, is_process_alive/1, length/1, link/1]).
--export([list_to_atom/1, list_to_binary/1, list_to_bitstr/1]).
+-export([list_to_atom/1, list_to_binary/1]).
-export([list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1]).
-export([list_to_integer/1, list_to_integer/2]).
-export([list_to_pid/1, list_to_tuple/1, loaded/0]).
@@ -361,25 +361,15 @@ binary_to_list(_Binary, _Start, _Stop) ->
%% binary_to_term/1
-spec binary_to_term(Binary) -> term() when
Binary :: ext_binary().
-binary_to_term(Binary) ->
- %% This BIF may throw badarg while trapping
- try
- erts_internal:binary_to_term(Binary)
- catch
- error:Reason -> erlang:error(Reason,[Binary])
- end.
+binary_to_term(_Binary) ->
+ erlang:nif_error(undefined).
%% binary_to_term/2
-spec binary_to_term(Binary, Opts) -> term() when
Binary :: ext_binary(),
Opts :: [safe].
-binary_to_term(Binary, Opts) ->
- %% This BIF may throw badarg while trapping
- try
- erts_internal:binary_to_term(Binary,Opts)
- catch
- error:Reason -> erlang:error(Reason,[Binary,Opts])
- end.
+binary_to_term(_Binary, _Opts) ->
+ erlang:nif_error(undefined).
%% bit_size/1
%% Shadowed by erl_bif_types: erlang:bit_size/1
@@ -394,12 +384,6 @@ bit_size(_Bitstring) ->
bitsize(_P1) ->
erlang:nif_error(undefined).
-%% bitstr_to_list/1
--spec erlang:bitstr_to_list(P1) -> [byte() | bitstring()] when
- P1 :: bitstring().
-bitstr_to_list(_P1) ->
- erlang:nif_error(undefined).
-
%% bitstring_to_list/1
-spec bitstring_to_list(Bitstring) -> [byte() | bitstring()] when
Bitstring :: bitstring().
@@ -1082,12 +1066,6 @@ list_to_atom(_String) ->
list_to_binary(_IoList) ->
erlang:nif_error(undefined).
-%% list_to_bitstr/1
--spec erlang:list_to_bitstr(P1) -> bitstring() when
- P1 :: bitstring_list().
-list_to_bitstr(_P1) ->
- erlang:nif_error(undefined).
-
%% list_to_bitstring/1
-spec list_to_bitstring(BitstringList) -> bitstring() when
BitstringList :: bitstring_list().
@@ -1745,9 +1723,9 @@ is_pid(_Term) ->
erlang:nif_error(undefined).
%% Shadowed by erl_bif_types: erlang:is_map/1
--spec is_map(Map) -> boolean() when
- Map :: map().
-is_map(_Map) ->
+-spec is_map(Term) -> boolean() when
+ Term :: term().
+is_map(_Term) ->
erlang:nif_error(undefined).
%% Shadowed by erl_bif_types: erlang:is_port/1
@@ -2264,7 +2242,6 @@ tuple_to_list(_Tuple) ->
(modified_timing_level) -> integer() | undefined;
(multi_scheduling) -> disabled | blocked | enabled;
(multi_scheduling_blockers) -> [PID :: pid()];
- (otp_correction_package) -> string();
(otp_release) -> string();
(port_count) -> non_neg_integer();
(port_limit) -> pos_integer();
@@ -2287,6 +2264,7 @@ tuple_to_list(_Tuple) ->
(system_architecture) -> string();
(threads) -> boolean();
(thread_pool_size) -> non_neg_integer();
+ (tolerant_timeofday) -> enabled | disabled;
(trace_control_word) -> non_neg_integer();
(update_cpu_info) -> changed | unchanged;
(version) -> string();
diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src
index fd3e8cb692..a15da3a421 100644
--- a/erts/preloaded/src/erts.app.src
+++ b/erts/preloaded/src/erts.app.src
@@ -35,7 +35,8 @@
{registered, []},
{applications, []},
{env, []},
- {mod, {erts, []}}
+ {mod, {erts, []}},
+ {runtime_dependencies, ["stdlib-2.0", "kernel-3.0", "sasl-2.4"]}
]}.
%% vim: ft=erlang
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index edcd50c77e..2c5bd82cf0 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -29,8 +29,8 @@
-module(erts_internal).
-export([await_port_send_result/3]).
--export([binary_to_term/1, binary_to_term/2]).
-export([cmp_term/2]).
+-export([map_to_tuple_keys/1]).
-export([port_command/3, port_connect/2, port_close/1,
port_control/3, port_call/3, port_info/1, port_info/2]).
@@ -161,17 +161,6 @@ request_system_task(_Pid, _Prio, _Request) ->
check_process_code(_Module, _OptionList) ->
erlang:nif_error(undefined).
--spec binary_to_term(Binary) -> term() when
- Binary :: binary().
-binary_to_term(_Binary) ->
- erlang:nif_error(undefined).
-
--spec binary_to_term(Binary, Opts) -> term() when
- Binary :: binary(),
- Opts :: [safe].
-binary_to_term(_Binary, _Opts) ->
- erlang:nif_error(undefined).
-
%% term compare where integer() < float() = true
-spec cmp_term(A,B) -> Result when
@@ -181,3 +170,11 @@ binary_to_term(_Binary, _Opts) ->
cmp_term(_A,_B) ->
erlang:nif_error(undefined).
+
+%% return the internal key tuple for map keys
+-spec map_to_tuple_keys(M) -> Keys when
+ M :: map(),
+ Keys :: tuple().
+
+map_to_tuple_keys(_M) ->
+ erlang:nif_error(undefined).
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index ab8464956c..e95e11b3e6 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -1041,7 +1041,7 @@ start_em([]) -> ok.
start_it([]) ->
ok;
start_it({eval,Bin}) ->
- Str = binary_to_list(Bin),
+ Str = b2s(Bin),
{ok,Ts,_} = erl_scan:string(Str),
Ts1 = case reverse(Ts) of
[{dot,_}|_] -> Ts;
diff --git a/erts/start_scripts/no_dot_erlang.rel.src b/erts/start_scripts/no_dot_erlang.rel.src
index 03b64ebf1a..6208572c00 100644
--- a/erts/start_scripts/no_dot_erlang.rel.src
+++ b/erts/start_scripts/no_dot_erlang.rel.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -16,6 +16,6 @@
%%
%% %CopyrightEnd%
%%
-{release, {"OTP APN 181 01","%SYS_VSN%"}, {erts, "%ERTS_VSN%"},
+{release, {"Erlang/OTP","%SYS_VSN%"}, {erts, "%ERTS_VSN%"},
[{kernel,"%KERNEL_VSN%"},
{stdlib,"%STDLIB_VSN%"}]}.
diff --git a/erts/start_scripts/start_all_example.rel.src b/erts/start_scripts/start_all_example.rel.src
index 581eb2eb0b..2a1cabe7bb 100644
--- a/erts/start_scripts/start_all_example.rel.src
+++ b/erts/start_scripts/start_all_example.rel.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -16,7 +16,7 @@
%%
%% %CopyrightEnd%
%%
-{release, {"OTP APN 181 01","%SYS_VSN%"}, {erts, "%ERTS_VSN%"},
+{release, {"Erlang/OTP","%SYS_VSN%"}, {erts, "%ERTS_VSN%"},
[{kernel,"%KERNEL_VSN%"},
{stdlib,"%STDLIB_VSN%"},
{sasl, "%SASL_VSN%"},
diff --git a/erts/start_scripts/start_clean.rel.src b/erts/start_scripts/start_clean.rel.src
index d2df422c51..e229721e36 100644
--- a/erts/start_scripts/start_clean.rel.src
+++ b/erts/start_scripts/start_clean.rel.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -16,6 +16,6 @@
%%
%% %CopyrightEnd%
%%
-{release, {"OTP APN 181 01","%SYS_VSN%"}, {erts, "%ERTS_VSN%"},
+{release, {"Erlang/OTP","%SYS_VSN%"}, {erts, "%ERTS_VSN%"},
[{kernel,"%KERNEL_VSN%"},
{stdlib,"%STDLIB_VSN%"}]}.
diff --git a/erts/start_scripts/start_sasl.rel.src b/erts/start_scripts/start_sasl.rel.src
index e521e8df91..e68a34af76 100644
--- a/erts/start_scripts/start_sasl.rel.src
+++ b/erts/start_scripts/start_sasl.rel.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -16,7 +16,7 @@
%%
%% %CopyrightEnd%
%%
-{release, {"OTP APN 181 01","%SYS_VSN%"}, {erts, "%ERTS_VSN%"},
+{release, {"Erlang/OTP","%SYS_VSN%"}, {erts, "%ERTS_VSN%"},
[{kernel,"%KERNEL_VSN%"},
{stdlib,"%STDLIB_VSN%"},
{sasl, "%SASL_VSN%"}]}.
diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl
index 8e4a1a4b1c..229d10ccee 100644
--- a/erts/test/otp_SUITE.erl
+++ b/erts/test/otp_SUITE.erl
@@ -24,7 +24,7 @@
-export([undefined_functions/1,deprecated_not_in_obsolete/1,
obsolete_but_not_deprecated/1,call_to_deprecated/1,
call_to_size_1/1,strong_components/1,
- erl_file_encoding/1,xml_file_encoding/1]).
+ erl_file_encoding/1,xml_file_encoding/1,runtime_dependencies/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -36,7 +36,8 @@ all() ->
[undefined_functions, deprecated_not_in_obsolete,
obsolete_but_not_deprecated, call_to_deprecated,
call_to_size_1, strong_components,
- erl_file_encoding, xml_file_encoding].
+ erl_file_encoding, xml_file_encoding,
+ runtime_dependencies].
groups() ->
[].
@@ -327,7 +328,9 @@ erl_file_encoding(_Config) ->
Wc = filename:join([Root,"**","*.erl"]),
ErlFiles = ordsets:subtract(ordsets:from_list(filelib:wildcard(Wc)),
release_files(Root, "*.erl")),
+ {ok, MP} = re:compile(".*lib/(ic)|(orber)|(cos).*", [unicode]),
Fs = [F || F <- ErlFiles,
+ filter_use_latin1_coding(F, MP),
case epp:read_encoding(F) of
none -> false;
_ -> true
@@ -341,6 +344,14 @@ erl_file_encoding(_Config) ->
?t:fail()
end.
+filter_use_latin1_coding(F, MP) ->
+ case re:run(F, MP) of
+ nomatch ->
+ true;
+ {match, _} ->
+ false
+ end.
+
xml_file_encoding(_Config) ->
XmlFiles = xml_files(),
Fs = [F || F <- XmlFiles, is_bad_encoding(F)],
@@ -380,6 +391,121 @@ is_bad_encoding(File) ->
true
end.
+runtime_dependencies(Config) ->
+ %% Ignore applications intentionally not declaring dependencies
+ %% found by xref.
+ IgnoreApps = [diameter],
+
+
+ %% Verify that (at least) OTP application runtime dependencies found
+ %% by xref are listed in the runtime_dependencies field of the .app file
+ %% of each application.
+ Server = ?config(xref_server, Config),
+ {ok, AE} = xref:q(Server, "AE"),
+ SAE = lists:keysort(1, AE),
+ put(ignored_failures, []),
+ {AppDep, AppDeps} = lists:foldl(fun ({App, App}, Acc) ->
+ Acc;
+ ({App, Dep}, {undefined, []}) ->
+ {{App, [Dep]}, []};
+ ({App, Dep}, {{App, Deps}, AppDeps}) ->
+ {{App, [Dep|Deps]}, AppDeps};
+ ({App, Dep}, {AppDep, AppDeps}) ->
+ {{App, [Dep]}, [AppDep | AppDeps]}
+ end,
+ {undefined, []},
+ SAE),
+ [] = lists:filter(fun ({missing_runtime_dependency,
+ AppFile,
+ common_test}) ->
+ %% The test_server app is contaminated by
+ %% common_test when run in a source tree. It
+ %% should however *not* be contaminated
+ %% when run in an installation.
+ case {filename:basename(AppFile),
+ is_run_in_src_tree()} of
+ {"test_server.app", true} ->
+ false;
+ _ ->
+ true
+ end;
+ (_) ->
+ true
+ end,
+ check_apps_deps([AppDep|AppDeps], IgnoreApps)),
+ case IgnoreApps of
+ [] ->
+ ok;
+ _ ->
+ Comment = lists:flatten(io_lib:format("Ignored applications: ~p "
+ "Ignored failures: ~p",
+ [IgnoreApps,
+ get(ignored_failures)])),
+ {comment, Comment}
+ end.
+
+is_run_in_src_tree() ->
+ %% At least currently run_erl is not present in <code-root>/bin
+ %% in the source tree, but present in <code-root>/bin of an
+ %% ordinary installation.
+ case file:read_file_info(filename:join([code:root_dir(),
+ "bin",
+ "run_erl"])) of
+ {ok, _} -> false;
+ {error, _} -> true
+ end.
+
+have_rdep(_App, [], _Dep) ->
+ false;
+have_rdep(App, [RDep | RDeps], Dep) ->
+ [AppStr, _VsnStr] = string:tokens(RDep, "-"),
+ case Dep == list_to_atom(AppStr) of
+ true ->
+ io:format("~p -> ~s~n", [App, RDep]),
+ true;
+ false ->
+ have_rdep(App, RDeps, Dep)
+ end.
+
+check_app_deps(_App, _AppFile, _AFDeps, [], _IgnoreApps) ->
+ [];
+check_app_deps(App, AppFile, AFDeps, [XRDep | XRDeps], IgnoreApps) ->
+ ResOtherDeps = check_app_deps(App, AppFile, AFDeps, XRDeps, IgnoreApps),
+ case have_rdep(App, AFDeps, XRDep) of
+ true ->
+ ResOtherDeps;
+ false ->
+ Failure = {missing_runtime_dependency, AppFile, XRDep},
+ case lists:member(App, IgnoreApps) of
+ true ->
+ put(ignored_failures, [Failure | get(ignored_failures)]),
+ ResOtherDeps;
+ false ->
+ [Failure | ResOtherDeps]
+ end
+ end.
+
+check_apps_deps([], _IgnoreApps) ->
+ [];
+check_apps_deps([{App, Deps}|AppDeps], IgnoreApps) ->
+ ResOtherApps = check_apps_deps(AppDeps, IgnoreApps),
+ AppFile = code:where_is_file(atom_to_list(App) ++ ".app"),
+ {ok,[{application, App, Info}]} = file:consult(AppFile),
+ case lists:keyfind(runtime_dependencies, 1, Info) of
+ {runtime_dependencies, RDeps} ->
+ check_app_deps(App, AppFile, RDeps, Deps, IgnoreApps)
+ ++ ResOtherApps;
+ false ->
+ Failure = {missing_runtime_dependencies_key, AppFile},
+ case lists:member(App, IgnoreApps) of
+ true ->
+ put(ignored_failures, [Failure | get(ignored_failures)]),
+ ResOtherApps;
+ false ->
+ [Failure | ResOtherApps]
+ end
+ end.
+
%%%
%%% Common help functions.
%%%
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 081fb66398..fff334c89f 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -17,7 +17,7 @@
# %CopyrightEnd%
#
-VSN = 6.0
+VSN = 6.0.2
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/asn1/doc/src/asn1_ug.xml b/lib/asn1/doc/src/asn1_ug.xml
index ee54fdffd7..020e58c615 100644
--- a/lib/asn1/doc/src/asn1_ug.xml
+++ b/lib/asn1/doc/src/asn1_ug.xml
@@ -34,23 +34,25 @@
<section>
<title>Features</title>
- <p>The Asn1 application provides:
- </p>
+ <p>The Asn1 application provides:</p>
<list type="bulleted">
<item>An ASN.1 compiler for Erlang, which generates encode and
decode functions to be used by Erlang programs sending and
receiving ASN.1 specified data.</item>
<item>Run-time functions used by the generated code.</item>
- <item>The supported encoding rules are:
+ <item>Support for the following encoding rules:
<list>
<item>
Basic Encoding Rules (<em>BER</em>)
</item>
<item>
- Distinguished Encoding Rules (<em>DER</em>), a specialized form of BER that is used in security-conscious applications.
+ Distinguished Encoding Rules (<em>DER</em>), a specialized
+ form of BER that is used in security-conscious
+ applications.
</item>
<item>
- Packed Encoding Rules (<em>PER</em>) both the aligned and unaligned variant.
+ Packed Encoding Rules (<em>PER</em>); both the aligned and
+ unaligned variant.
</item>
</list>
</item>
@@ -59,71 +61,41 @@
<section>
<title>Overview</title>
- <p>ASN.1 (Abstract Syntax Notation 1) is a formal language for describing data structures to be exchanged between distributed computer systems.
- The purpose of ASN.1 is to have
- a platform and programming language independent notation to express
- types using a
- standardized set of rules for the transformation of values of
- a defined type, into a stream of bytes. This stream of bytes
- can then be sent on a communication channel set up by the
- lower layers in the stack of communication protocols e.g.
- TCP/IP or encapsulated within UDP packets. This way, two
- different applications written in two completely different
- programming languages running on different computers with
- different internal representation of data can exchange
- instances of structured data types (instead of exchanging
- bytes or bits). This makes programming faster and easier since no code
- has to be written to process the transport format of the
- data.
- </p>
- <p>To write a network application which processes ASN.1 encoded
- messages, it is prudent and sometimes essential to have a set
- of off-line development tools such as an ASN.1 compiler which
- can generate the encode and decode logic for the specific ASN.1
- data types. It is also necessary to combine this with some
- general language-specific runtime support for ASN.1 encoding and
- decoding.
- </p>
- <p>The ASN.1 compiler must be directed towards a target language
- or a set of closely related languages. This manual describes a
- compiler which is directed towards the functional language
- Erlang. In order to use this compiler, familiarity with the
- language Erlang is essential. Therefore, the runtime support for ASN.1 is
- also closely related to the language Erlang and
- consist of a number of functions, which the
- compiler uses. The types in ASN.1 and how to represent
- values of those types in Erlang are described in this manual.
- </p>
- <p>The following document is structured so that the first part describes
- how to use ASN.1 compiler, and then there are descriptions of all
- the primitive and constructed ASN.1 types and their representation
- in Erlang,
- </p>
+ <p>ASN.1 (Abstract Syntax Notation One) is a formal language for
+ describing data structures to be exchanged between distributed
+ computer systems. The purpose of ASN.1 is to have a platform
+ and programming language independent notation to express types
+ using a standardized set of rules for the transformation of
+ values of a defined type into a stream of bytes. This stream of
+ bytes can then be sent on any type of communication
+ channel. This way, two applications written in different
+ programming languages running on different computers with
+ different internal representation of data can exchange instances
+ of structured data types.</p>
</section>
<section>
<title>Prerequisites</title>
- <p>It is assumed that the reader is familiar with the ASN.1 notation
- as documented in the standard definition [<cite id="X.680"></cite>] which is
- the primary text. It may also be helpful, but not necessary,
- to read the standard definitions
- [<cite id="X.681"></cite>] [<cite id="X.682"></cite>] [<cite id="X.683"></cite>]
- [<cite id="X.690"></cite>] [<cite id="X.691"></cite>]. </p>
- <p>A very good book explaining those reference texts is
- [<cite id="DUBUISSON"></cite>], free to download at
- <url href="http://www.oss.com/asn1/dubuisson.html">http://www.oss.com/asn1/dubuisson.html </url>.
+ <p>It is assumed that the reader is familiar with the ASN.1
+ notation as documented in the standard definition [<cite
+ id="X.680"></cite>] which is the primary text. It may also be
+ helpful, but not necessary, to read the standard definitions
+ [<cite id="X.681"></cite>] [<cite id="X.682"></cite>] [<cite
+ id="X.683"></cite>] [<cite id="X.690"></cite>] [<cite
+ id="X.691"></cite>]. </p>
+ <p>A good book explaining those reference texts is
+ [<cite id="DUBUISSON"></cite>], which is free to download at
+ <url href="http://www.oss.com/asn1/dubuisson.html">http://www.oss.com/asn1/dubuisson.html</url>.
</p>
</section>
<section>
- <title>Capability</title>
+ <title>Capabilities</title>
<p>This application covers all features of ASN.1 up to the 1997
- edition of the specification. In the 2002 edition of ASN.1 a number of
- new features where introduced of which some are supported while
- others are not. For example the
- ECN (Encoding Control Notation) and XML notation are still
- unsupported. Though, the other features of the 2002 edition are
- fully or partly supported as shown below:</p>
+ edition of the specification. In the 2002 edition of ASN.1 a
+ number of new features were introduced. The following features
+ of the 2002 edition are fully or partly supported as shown
+ below:</p>
<list type="bulleted">
<item>
<p>Decimal notation (e.g., "1.5e3") for REAL values. The
@@ -131,7 +103,7 @@
supported.</p>
</item>
<item>
- <p>The RELATIVE-OID type for relative object identifiers are
+ <p>The RELATIVE-OID type for relative object identifiers is
fully supported.</p>
</item>
<item>
@@ -141,16 +113,16 @@
constraint is not a PER-visible constraint.</p>
</item>
<item>
- <p>The subtype constraint by regular expressions (PATTERN) for character string types is parsed when compiling, but no further action is taken. This constraint is not a PER-visible constraint.</p>
+ <p>The subtype constraint by regular expressions (PATTERN)
+ for character string types is parsed when compiling, but no
+ further action is taken. This constraint is not a
+ PER-visible constraint.</p>
</item>
<item>
<p>Multiple-line comments as in C, <c>/* ... */</c>, are
supported.</p>
</item>
</list>
- <p>It should also be added here that the encoding formats
- supported are <em>BER</em>, <em>DER</em>, <em>PER aligned
- basic</em> variant and <em>PER unaligned basic</em> variant.</p>
</section>
</section>
@@ -162,19 +134,17 @@
<title>A First Example</title>
<p>The following example demonstrates the basic functionality used to run
the Erlang ASN.1 compiler.</p>
- <p>First, create a file called <c>People.asn</c> containing the following:</p>
+ <p>Create a file called <c>People.asn</c> containing the following:</p>
<pre>
-People DEFINITIONS IMPLICIT TAGS ::=
-
+People DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
-EXPORTS Person;
-
-Person ::= [PRIVATE 19] SEQUENCE {
- name PrintableString,
- location INTEGER {home(0),field(1),roving(2)},
- age INTEGER OPTIONAL }
+ Person ::= SEQUENCE {
+ name PrintableString,
+ location INTEGER {home(0),field(1),roving(2)},
+ age INTEGER OPTIONAL
+ }
END </pre>
- <p>This file (<c>people.asn</c>) must be compiled before it can be
+ <p>This file (<c>People.asn</c>) must be compiled before it can be
used.
The ASN.1 compiler checks that the syntax is correct and that the
text represents proper ASN.1 code before generating an abstract
@@ -186,14 +156,14 @@ END </pre>
The following shows how the compiler
can be called from the Erlang shell:</p>
<pre>
-1><input>asn1ct:compile("People", [ber]).</input>
+1><input> asn1ct:compile("People", [ber]).</input>
ok
2> </pre>
<p>The <c>verbose</c> option can be given to have information
about the generated files printed:</p>
<pre>
-2><input>asn1ct:compile("People", [ber,verbose]).</input>
+2><input> asn1ct:compile("People", [ber,verbose]).</input>
Erlang ASN.1 compiling "People.asn"
--{generated,"People.asn1db"}--
--{generated,"People.hrl"}--
@@ -201,17 +171,17 @@ Erlang ASN.1 compiling "People.asn"
ok
3> </pre>
- <p>The ASN.1 module People is now accepted and the abstract syntax tree
- is saved in the <c>People.asn1db</c> file, the
- generated Erlang code is compiled using the Erlang compiler and
- loaded into the Erlang runtime system. Now there is a user interface
- for <c>encode/2</c> and <c>decode/2</c> in the module People,
- which is invoked by:
- <br></br>
-<c><![CDATA['People':encode(<Type name>,<Value>),]]></c> <br></br>
-
+ <p>The ASN.1 module <c>People</c> is now accepted and the
+ abstract syntax tree is saved in the <c>People.asn1db</c> file;
+ the generated Erlang code is compiled using the Erlang compiler
+ and loaded into the Erlang run-time system. Now there is an API
+ for <c>encode/2</c> and <c>decode/2</c> in the module
+ <c>People</c>, which is invoked by: <br></br>
+ <c><![CDATA['People':encode(<Type name>, <Value>)]]></c>
+ <br></br>
or <br></br>
-<c><![CDATA['People':decode(<Type name>,<Value>),]]></c></p>
+<c><![CDATA['People':decode(<Type name>, <Value>)]]></c></p>
+
<p>Assume there is a network
application which receives instances of the ASN.1 defined
type Person, modifies and sends them back again:</p>
@@ -234,8 +204,7 @@ receive
constructed and encoded using
<c>'People':encode('Person',Answer)</c> which takes an
instance of a defined ASN.1 type and transforms it to a
- binary according to the BER or PER
- encoding-rules.
+ binary according to the BER or PER encoding rules.
<br></br>
The encoder and the decoder can also be run from
the shell.</p>
@@ -252,13 +221,13 @@ The encoder and the decoder can also be run from
<section>
<title>Module dependencies</title>
- <p>It is common that asn1 modules import defined types, values and
- other entities from another asn1 module.</p>
- <p>Earlier versions of the asn1 compiler required that modules that
+ <p>It is common that ASN.1 modules import defined types, values and
+ other entities from another ASN.1 module.</p>
+ <p>Earlier versions of the ASN.1 compiler required that modules that
were imported from had to be compiled before the module that
- imported. This caused problems when asn1 modules had circular
+ imported. This caused problems when ASN.1 modules had circular
dependencies.</p>
- <p>Now are referenced modules parsed when the compiler finds an
+ <p>Referenced modules are now parsed when the compiler finds an
entity that is imported. There will not be any code generated for
the referenced module. However, the compiled module rely on
that the referenced modules also will be compiled.</p>
@@ -310,7 +279,7 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn
</item>
<tag><c>-I IncludeDir</c></tag>
<item>
- <p>Where to search for <c>.asn1db</c> files and asn1
+ <p>Where to search for <c>.asn1db</c> files and ASN.1
source specs in order to resolve references to other
modules. This option can be repeated many times if there
are several places to search in. The compiler will always
@@ -322,26 +291,26 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn
</item>
<tag><c>+asn1config</c></tag>
<item>
- <p>This functionality works together with the flags
- <c>ber</c>. It enables the
+ <p>This functionality works together with the
+ <c>ber</c> option. It enables the
specialized decodes, see the <seealso marker="asn1_spec">Specialized Decode</seealso> chapter.
</p>
</item>
<tag><c>+undec_rest</c></tag>
<item>
- <p>A buffer that holds a message, being decoded may
- also have some following bytes. Now it is possible to get
- those following bytes returned together with the decoded
- value. If an asn1 spec is compiled with this option a tuple
- <c>{ok,Value,Rest}</c> is returned. <c>Rest</c> may be a
- list or a binary. Earlier versions of the compiler ignored
- those following bytes.</p>
+ <p>A buffer that holds a message being decoded may also have
+ trailing bytes. If those trailing bytes are important they
+ can be returned along with the decoded value by compiling
+ the ASN.1 specification with the <c>+undec_rest</c> option.
+ The return value from the decoder will be
+ <c>{ok,Value,Rest}</c> where <c>Rest</c> is a binary
+ containing the trailing bytes.</p>
</item>
<tag><c>+'Any Erlc Option'</c></tag>
<item>
<p>You may add any option to the Erlang compiler when
compiling the generated Erlang files. Any option
- unrecognised by the asn1 compiler will be passed to the
+ unrecognized by the ASN.1 compiler will be passed to the
Erlang compiler.</p>
</item>
</taglist>
@@ -366,10 +335,6 @@ asn1ct:compile("H323-MESSAGES.asn1",[ber]). </pre>
asn1ct:compile("H323-MESSAGES.asn1",[per]). </pre>
<p>The generic encode and decode functions can be invoked like this:</p>
<pre>
-asn1ct:encode('H323-MESSAGES','SomeChoiceType',{call,"octetstring"}).
-asn1ct:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
- <p>Or, preferable like:</p>
- <pre>
'H323-MESSAGES':encode('SomeChoiceType',{call,"octetstring"}).
'H323-MESSAGES':decode('SomeChoiceType',Bytes). </pre>
</section>
@@ -389,7 +354,7 @@ asn1ct:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
compile time appear on the screen together with
a line number indicating where in the source file the error
was detected. If no errors are found, an Erlang ASN.1 module will
- be created as default.</p>
+ be created.</p>
<p>The run-time encoders and decoders execute within a catch and
returns <c>{ok, Data}</c> or
<c>{error, {asn1, Description}}</c> where
@@ -400,18 +365,18 @@ asn1ct:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
<section>
<marker id="inlineExamples"></marker>
- <title>Multi File Compilation</title>
- <p>There are various reasons for using a multi file compilation:</p>
+ <title>Multi-file Compilation</title>
+ <p>There are various reasons for using multi-file compilation:</p>
<list type="bulleted">
- <item>You want to choose name for the generated module by
- any reason. Maybe you need to compile the same specs for
- different encoding/decoding standards.</item>
+ <item>You want to choose the name for the generated module,
+ perhaps because you need to compile the same specs for
+ different encoding rules.</item>
<item>You want only one resulting module.</item>
</list>
- <p>You need to specify which asn1 specs you will
+ <p>You need to specify which ASN.1 specs you will
compile in a module that must have the extension
<c>.set.asn</c>. You chose name of the module and provide the
- names of the asn1 specs. For instance, if you have the specs
+ names of the ASN.1 specs. For instance, if you have the specs
<c>File1.asn</c>, <c>File2.asn</c> and <c>File3.asn</c> your
module <c>MyModule.set.asn</c> will look like:</p>
<pre>
@@ -422,11 +387,45 @@ File3.asn </pre>
<code type="none">
~> erlc MyModule.set.asn </code>
<p>the result will be one merged module <c>MyModule.erl</c> with
- the generated code from the three asn1 specs.
+ the generated code from the three ASN.1 specs.
</p>
</section>
<section>
+ <title>A quick note about tags</title>
+
+ <p>Tags used to be important for all users of ASN.1, because it
+ was necessary to manually add tags to certain constructs in order
+ for the ASN.1 specification to be valid. Here is an example of
+ an old-style specification:</p>
+
+ <pre>
+Tags DEFINITIONS ::=
+BEGIN
+ Afters ::= CHOICE { cheese [0] IA5String,
+ dessert [1] IA5String }
+END </pre>
+
+ <p>Without the tags (the numbers in square brackets) the ASN.1
+ compiler would refuse to compile the file.</p>
+
+ <p>In 1994 the global tagging mode AUTOMATIC TAGS was introduced.
+ By putting AUTOMATIC TAGS in the module header, the ASN.1 compiler
+ will automatically add tags when needed. Here is the same
+ specification in AUTOMATIC TAGS mode:</p>
+
+ <pre>
+Tags DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+ Afters ::= CHOICE { cheese IA5String,
+ dessert IA5String }
+END
+</pre>
+
+ <p>Tags will not be mentioned any more in this manual.</p>
+ </section>
+
+ <section>
<marker id="ASN1Types"></marker>
<title>The ASN.1 Types</title>
<p>This section describes the ASN.1 types including their
@@ -497,7 +496,7 @@ Operational ::= BOOLEAN --ASN.1 definition </pre>
<p>In Erlang code it may look like:</p>
<pre>
Val = true,
-{ok,Bytes}=asn1rt:encode(MyModule,'Operational',Val), </pre>
+{ok,Bytes} = MyModule:encode('Operational', Val), </pre>
<p>Below follows a description of how
values of each type can be represented in Erlang.
</p>
@@ -563,20 +562,18 @@ T6value3 = white
<section>
<marker id="REAL"></marker>
<title>REAL</title>
- <p>In this version reals are not implemented. When they are,
- the following
- ASN.1 type is used:</p>
+ <p>The following ASN.1 type is used for real numbers:</p>
<pre>
R1 ::= REAL
</pre>
- <p>Can be assigned a value in Erlang as:</p>
+ <p>It can be assigned a value in Erlang as:</p>
<pre>
-R1value1 = 2.14,
+R1value1 = "2.14",
R1value2 = {256,10,-2},
</pre>
<p>In the last line note that the tuple {256,10,-2} is the real number
2.56 in a special notation, which will encode faster than simply
- stating the number as 2.56. The arity three tuple is
+ stating the number as <c>"2.56"</c>. The arity three tuple is
<c>{Mantissa,Base,Exponent}</c> i.e. Mantissa * Base^Exponent.</p>
</section>
@@ -736,13 +733,11 @@ O2Val = &lt;&lt;"must be exactly 28 chars...."&gt;&gt;,</pre>
specified for a type are especially important for PER, where
they affect the encoding.
</p>
- <p>Please note that <em>all</em> the Character strings are
- supported and it is possible to use the following ASN.1 type
- definitions:</p>
+ <p>Here are some examples:</p>
<pre>
Digs ::= NumericString (SIZE(1..3))
TextFile ::= IA5String (SIZE(0..64000)) </pre>
- <p>and the following Erlang assignments:</p>
+ <p>with corresponding Erlang assignments:</p>
<pre>
DigsVal1 = "456",
DigsVal2 = "123",
@@ -755,70 +750,86 @@ TextFileVal2 = [88,76,55,44,99,121 .......... a lot of characters here ....]
characters are all represented by quadruples beginning with
three zeros like {0,0,0,65} for the 'A' character. When
decoding a value for these strings the result is a list of
- quadruples, or integers when the value is an ASCII character.
- The following example shows how it works:</p>
- <p>In a file <c>PrimStrings.asn1</c> the type <c>BMP</c> is defined as
- <br></br>
-<c>BMP ::= BMPString</c> then using BER encoding (<c>ber</c>
- option)the input/output format will be:</p>
+ quadruples, or integers when the value is an ASCII character.</p>
+
+ <p>The following example shows how it works. We have the following
+ specification in the file <c>PrimStrings.asn1</c>.</p>
+ <pre>
+PrimStrings DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+ BMP ::= BMPString
+END
+ </pre>
+
+ <p>Encoding and decoding some strings:</p>
+
<pre>
-1> <input>{ok,Bytes1} = asn1rt:encode('PrimStrings','BMP',[{0,0,53,53},{0,0,45,56}]).</input>
-{ok,[30,4,"55-8"]}
-2> <input>asn1rt:decode('PrimStrings','BMP',list_to_binary(Bytes1)).</input>
+1> <input>asn1ct:compile('PrimStrings', [ber]).</input>
+ok
+2> <input>{ok,Bytes1} = 'PrimStrings':encode('BMP', [{0,0,53,53},{0,0,45,56}]).</input>
+{ok,&lt;&lt;30,4,53,54,45,56>>}
+3> <input>'PrimStrings':decode('BMP', Bytes1).</input>
{ok,[{0,0,53,53},{0,0,45,56}]}
-3> <input>{ok,Bytes2} = asn1rt:encode('PrimStrings','BMP',[{0,0,53,53},{0,0,0,65}]).</input>
-{ok,[30,4,[53,53,0,65]]}
-4> <input>asn1rt:decode('PrimStrings','BMP',list_to_binary(Bytes2)).</input>
+4> <input>{ok,Bytes2} = 'PrimStrings':encode('BMP', [{0,0,53,53},{0,0,0,65}]).</input>
+{ok,&lt;&lt;30,4,53,53,0,65>>}
+5> <input>'PrimStrings':decode('BMP', Bytes2).</input>
{ok,[{0,0,53,53},65]}
-5> <input>{ok,Bytes3} = asn1rt:encode('PrimStrings','BMP',"BMP string").</input>
-{ok,[30,20,[0,66,0,77,0,80,0,32,0,115,0,116,0,114,0,105,0,110,0,103]]}
-6> <input>asn1rt:decode('PrimStrings','BMP',list_to_binary(Bytes3)).</input>
+6> <input>{ok,Bytes3} = 'PrimStrings':encode('BMP', "BMP string").</input>
+{ok,&lt;&lt;30,20,0,66,0,77,0,80,0,32,0,115,0,116,0,114,0,105,0,110,0,103>>}
+7> <input>'PrimStrings':decode('BMP', Bytes3).</input>
{ok,"BMP string"} </pre>
- <p>The UTF8String is represented in Erlang as a list of integers,
- where each integer represents the unicode value of one
- character. When a value shall be encoded one first has to
- transform it to a UTF8 encoded binary, then it can be encoded by
- asn1. When decoding the result is a UTF8 encoded binary, which
- may be transformed to an integer list. The transformation
- functions, <c>utf8_binary_to_list</c> and
- <c>utf8_list_to_binary</c>, are in the <c>asn1rt</c> module. In
- the example below we assume an asn1 definition <c>UTF ::= UTF8String</c> in a module <c>UTF.asn</c>:</p>
+
+ <p>The UTF8String type is represented as a UTF-8 encoded binary in
+ Erlang. Such binaries can be created directly using the binary syntax
+ or by converting from a list of Unicode code points using the
+ <c>unicode:characters_to_binary/1</c> function.</p>
+
+ <p>Here are some examples showing how UTF-8 encoded binaries can
+ be created and manipulated:</p>
+
+ <pre>
+1> <input>Gs = "Мой маленький Гном".</input>
+[1052,1086,1081,32,1084,1072,1083,1077,1085,1100,1082,1080,
+ 1081,32,1043,1085,1086,1084]
+2> <input>Gbin = unicode:characters_to_binary(Gs).</input>
+&lt;&lt;208,156,208,190,208,185,32,208,188,208,176,208,187,208,
+ 181,208,189,209,140,208,186,208,184,208,185,32,208,147,
+ 208,...>>
+3> <input>Gbin = &lt;&lt;"Мой маленький Гном"/utf8>>.</input>
+&lt;&lt;208,156,208,190,208,185,32,208,188,208,176,208,187,208,
+ 181,208,189,209,140,208,186,208,184,208,185,32,208,147,
+ 208,...>>
+4> <input>Gs = unicode:characters_to_list(Gbin).</input>
+[1052,1086,1081,32,1084,1072,1083,1077,1085,1100,1082,1080,
+ 1081,32,1043,1085,1086,1084]
+ </pre>
+
+ <p>See the <seealso marker="stdlib:unicode">unicode</seealso> module
+ for more details.</p>
+
+ <p>In the following example we will use this ASN.1 specification:</p>
<pre>
-1> <input>asn1ct:compile('UTF',[ber]).</input>
-Erlang ASN.1 version "1.4.3.3" compiling "UTF.asn"
-Compiler Options: [ber]
---{generated,"UTF.asn1db"}--
---{generated,"UTF.erl"}--
+UTF DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+ UTF ::= UTF8String
+END
+ </pre>
+
+ <p>Encoding and decoding a string with Unicode characters:</p>
+
+ <pre>
+5> <input>asn1ct:compile('UTF', [ber]).</input>
+ok
+6> <input>{ok,Bytes1} = 'UTF':encode('UTF', &lt;&lt;"Гном"/utf8>>).</input>
+{ok,&lt;&lt;12,8,208,147,208,189,208,190,208,188>>}
+7> <input>{ok,Bin1} = 'UTF':decode('UTF', Bytes1).</input>
+{ok,&lt;&lt;208,147,208,189,208,190,208,188>>}
+8> <input>io:format("~ts\n", [Bin1]).</input>
+Гном
ok
-2> <input>UTF8Val1 = "hello".</input>
-"hello"
-3> <input>{ok,UTF8bin1} = asn1rt:utf8_list_to_binary(UTF8Val1).</input>
-{ok,&lt;&lt;104,101,108,108,111&gt;&gt;}
-4> <input>{ok,B}='UTF':encode('UTF',UTF8bin1).</input>
-{ok,[12,
- 5,
- &lt;&lt;104,101,108,108,111&gt;&gt;]}
-5> <input>Bin = list_to_binary(B).</input>
-&lt;&lt;12,5,104,101,108,108,111&gt;&gt;
-6> <input>{ok,UTF8bin1}='UTF':decode('UTF',Bin).</input>
-{ok,&lt;&lt;104,101,108,108,111&gt;&gt;}
-7> <input>asn1rt:utf8_binary_to_list(UTF8bin1).</input>
-{ok,"hello"}
-8> <input>UTF8Val2 = [16#00,16#100,16#ffff,16#ffffff].</input>
-[0,256,65535,16777215]
-9> <input>{ok,UTF8bin2} = asn1rt:utf8_list_to_binary(UTF8Val2).</input>
-{ok,&lt;&lt;0,196,128,239,191,191,248,191,191,191,191&gt;&gt;}
-10> <input>{ok,B2} = 'UTF':encode('UTF',UTF8bin2).</input>
-{ok,[12,
- 11,
- &lt;&lt;0,196,128,239,191,191,248,191,191,191,191&gt;&gt;]}
-11> <input>Bin2 = list_to_binary(B2).</input>
-&lt;&lt;12,11,0,196,128,239,191,191,248,191,191,191,191&gt;&gt;
-12> <input>{ok,UTF8bin2} = 'UTF':decode('UTF',Bin2).</input>
-{ok,&lt;&lt;0,196,128,239,191,191,248,191,191,191,191&gt;&gt;}
-13> <input>asn1rt:utf8_binary_to_list(UTF8bin2).</input>
-{ok,[0,256,65535,16777215]}
-14> </pre>
+9> <input>unicode:characters_to_list(Bin1).</input>
+[1043,1085,1086,1084]
+ </pre>
</section>
<section>
@@ -853,9 +864,11 @@ OidVal1 = {1,2,55},
<section>
<marker id="Object Descriptor"></marker>
<title>Object Descriptor</title>
- <p>Values of this type can be assigned a value as an ordinary string i.e. <br></br>
+ <p>Values of this type can be assigned a value as an ordinary string
+ like this:</p>
- "This is the value of an Object descriptor"</p>
+ <pre>
+ "This is the value of an Object descriptor"</pre>
</section>
<section>
@@ -898,19 +911,31 @@ Pdu ::= SEQUENCE {
<pre>
MyPdu = #'Pdu'{a=22,b=77.99,c={0,1,2,3,4},d='NULL'}. </pre>
<p>The decode functions will return a record as result when decoding
- a <c>SEQUENCE</c> or a <c>SET</c>.
- <marker id="DEFAULT"></marker>
-</p>
- <p>A <c>SEQUENCE</c> and a <c>SET</c> may contain a component with a
- <c>DEFAULT</c> key word followed by the actual value that is the
- default value. In case of BER encoding it is optional to encode the
- value if it equals the default value. If the application uses the
- atom asn1_DEFAULT as value or if the value is a primitive value
- that equals the default value the encoding omits the bytes for
- this value, which is more efficient and it results in fever
- bytes to send to the receiving application.</p>
- <p>For instance, if the following types exists in a file "File.asn":</p>
+ a <c>SEQUENCE</c> or a <c>SET</c>.</p>
+
+ <p>A <c>SEQUENCE</c> and a <c>SET</c> may contain a component
+ with a <c>DEFAULT</c> key word followed by the actual value that
+ is the default value. The <c>DEFAULT</c> keyword means that the
+ application doing the encoding can omit encoding of the value,
+ thus resulting in fewer bytes to send to the receiving
+ application.</p>
+
+ <p>An application can use the atom <c>asn1_DEFAULT</c> to indicate
+ that the encoding should be omitted for that position in
+ the SEQUENCE.</p>
+
+ <p>Depending on the encoding rules, the encoder may also compare
+ the given value to the default value and automatically omit the
+ encoding if they are equal. How much effort the encoder makes to
+ to compare the values depends on the encoding rules. The DER
+ encoding rules forbids encoding a value equal to the default value,
+ so it has a more thorough and time-consuming comparison than the
+ encoders for the other encoding rules.</p>
+
+ <p>In the following example we will use this ASN.1 specification:</p>
<pre>
+File DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
Seq1 ::= SEQUENCE {
a INTEGER DEFAULT 1,
b Seq2 DEFAULT {aa TRUE, bb 15}
@@ -920,131 +945,50 @@ Seq2 ::= SEQUENCE {
aa BOOLEAN,
bb INTEGER
}
- </pre>
- <p>Some values and the corresponding encoding in an Erlang terminal
- is shown below:</p>
+
+Seq3 ::= SEQUENCE {
+ bs BIT STRING {a(0), b(1), c(2)} DEFAULT {a, c}
+}
+END </pre>
+ <p>Here is an example where the BER encoder is able to omit encoding
+ of the default values:</p>
<pre>
-1> <input>asn1ct:compile('File').</input>
-Erlang ASN.1 version "1.3.2" compiling "File.asn1"
-Compiler Options: []
---{generated,"File.asn1db"}--
---{generated,"File.hrl"}--
---{generated,"File.erl"}--
+1> <input>asn1ct:compile('File', [ber]).</input>
ok
-2> <input>'File':encode('Seq1',{'Seq1',asn1_DEFAULT,asn1_DEFAULT}).</input>
-{ok,["0",[0],[[],[]]]}
-3> <input>lists:flatten(["0",[0],[[],[]]]).</input>
-[48,0]
-4> <input>'File':encode('Seq1',{'Seq1',1,{'Seq2',true,15}}).</input>
-{ok,["0","\\b",[[],["\\241",[6],[[[128],[1],"\\377"],[[129],[1],[15]]]]]]}
-5> <input>lists:flatten(["0","\\b",[[],["\\241",[6],[[[128],[1],"\\377"],[[129],[1],[15]]]]]]).</input>
-[48,8,161,6,128,1,255,129,1,15]
-6> </pre>
- <p>The result after command line 3, in the example above,shows that the
- encoder omits the encoding of default values when they are specific
- by asn1_DEFAULT. Line 5 shows that even primitive values that equals
- the default value are detected and not encoded. But the constructed
- value of component <c>b</c> in <c>Seq1</c> is not recognized as the
- default value. Checking of default values in <c>BER</c> is not done
- in case of complex values, because it would be to expensive.
- <marker id="DEFAULT DER"></marker>
-</p>
- <p>But, the DER encoding format has stronger requirements regarding
- default values both for SET and SEQUENCE. A more elaborate and time
- expensive check of default values will take place. The following is
- an example with the same types and values as above but with der
- encoding format.</p>
- <pre>
-1> <input>asn1ct:compile('File',[der]).</input>
-Erlang ASN.1 version "1.3.2" compiling "File.asn1"
-Compiler Options: [der]
---{generated,"File.asn1db"}--
---{generated,"File.hrl"}--
---{generated,"File.erl"}--
+2> <input>'File':encode('Seq1', {'Seq1',asn1_DEFAULT,asn1_DEFAULT}).</input>
+{ok,&lt;&lt;48,0>>}
+3> <input>'File':encode('Seq1', {'Seq1',1,{'Seq2',true,15}}).</input>
+{ok,&lt;&lt;48,0>>} </pre>
+
+ <p>And here is an example with a named BIT STRING where the BER
+ encoder will not omit the encoding:</p>
+ <pre>
+4> <input>'File':encode('Seq3', {'Seq3',asn1_DEFAULT).</input>
+{ok,&lt;&lt;48,0>>}
+5> <input>'File':encode('Seq3', {'Seq3',&lt;&lt;16#101:3>>).</input>
+{ok,&lt;&lt;48,4,128,2,5,160>>} </pre>
+
+ <p>The DER encoder will omit the encoding for the same BIT STRING:</p>
+ <pre>
+6> <input>asn1ct:compile('File', [ber,der]).</input>
ok
-2> <input>'File':encode('Seq1',{'Seq1',asn1_DEFAULT,asn1_DEFAULT}).</input>
-{ok,["0",[0],[[],[]]]}
-3> <input>lists:flatten(["0",[0],[[],[]]]).</input>
-[48,0]
-4> <input>'File':encode('Seq1',{'Seq1',1,{'Seq2',true,15}}).</input>
-{ok,["0",[0],[[],[]]]}
-5> <input>lists:flatten(["0",[0],[[],[]]]).</input>
-[48,0]
-6>
- </pre>
- <p>Line 5 shows that even values of constructed types is checked and if
- it equals the default value it will not be encoded.</p>
+7> <input>'File':encode('Seq3', {'Seq3',asn1_DEFAULT).</input>
+{ok,&lt;&lt;48,0>>}
+8> <input>'File':encode('Seq3', {'Seq3',&lt;&lt;16#101:3>>).</input>
+{ok,&lt;&lt;48,0>>} </pre>
</section>
<section>
<marker id="SET"></marker>
<title>SET</title>
- <p>The SET type is an unusual construct and normally the SEQUENCE
- type is more appropriate to use. Set is also inefficient compared with SEQUENCE, as the components can be in any order. Hence, it must be possible
- to distinguish every component in 'SET', both when
- encoding and decoding a value of a type defined to be a SET.
- The tags of all components must be different from each other
- in order to be easily recognizable.</p>
- <p>A SET may be defined as:</p>
- <pre>
-Pdu2 ::= SET {
- a INTEGER,
- b BOOLEAN,
- c ENUMERATED {on(0),off(1)} } </pre>
- <p>A SET is represented as an Erlang record.
- For each SEQUENCE and <c>SET</c> in
- an ASN.1 module an Erlang record declaration is generated. For
- <c>Pdu2</c> above a record is defined like this:</p>
- <pre>
--record('Pdu2',{a, b, c}). </pre>
- <p>The record declarations for a module <c>M</c> are placed in a
- separate <c>M.hrl</c> file.</p>
- <p>Values can be assigned in Erlang as demonstrated below:</p>
- <pre>
-V = #'Pdu2'{a=44,b=false,c=off}. </pre>
- <p>The decode functions will return a record as result when decoding
- a SET.
- </p>
- <p>The difference between SET and SEQUENCE is that the order of
- the components (in the BER encoded format) is undefined for SET
- and defined as the lexical order from the ASN.1 definition for
- SEQUENCE. The ASN.1 compiler for Erlang will always encode a
- SET in the lexical order. The decode routines can handle SET
- components encoded in any order but will always return the
- result as a record. Since all components of the SET must be
- distinguishable both in the encoding phase as well as the
- decoding phase the following type is not allowed in a module
- with EXPLICIT or IMPLICIT as tag-default :</p>
- <p></p>
- <pre>
-Bad ::= SET {i INTEGER,
- j INTEGER } </pre>
- <p>The ASN.1 to Erlang compiler rejects the above type. We
- shall not explain the concept of tag further here, we refer to
- [<cite id="X.680"></cite>].
- </p>
- <p>Encoding of a SET with components with DEFAULT values behaves
- similar as a SEQUENCE, <seealso marker="#DEFAULT">see above</seealso>. The DER encoding format restrictions on DEFAULT
- values is the same for SET as for SEQUENCE, and is supported by
- the compiler, <seealso marker="#DEFAULT DER">see above</seealso>.</p>
- <p>Moreover, in DER the elements of a SET will be sorted. If a
- component is an un-tagged choice the sorting have to take place
- in run-time. This fact emphasizes the following recommendation
- if DER encoding format is used.</p>
- <p>The concept of SET is an unusual
- construct and one cannot think of one single application
- where the set type is essential. (Imagine if someone
- "invented'' the shuffled array in 'C') People tend to think
- that 'SET' sounds nicer and more mathematical than 'SEQUENCE'
- and hence use it when 'SEQUENCE' would have been more
- appropriate. It is also most inefficient, since every correct
- implementation of SET must always be prepared to accept the
- components in any order. So, if possible use SEQUENCE instead
- of SET.</p>
+ <p>In Erlang, the SET type is used exactly as SEQUENCE. Note
+ that if the BER or DER encoding rules are used, decoding a
+ SET is slower than decoding a SEQUENCE because the components
+ must be sorted.</p>
</section>
<section>
- <title>Notes about Extend-ability for SEQUENCE and SET</title>
+ <title>Notes about extensibility for SEQUENCE and SET</title>
<p>When a SEQUENCE or SET contains an extension marker and
extension components like this:</p>
<pre>
@@ -1071,51 +1015,28 @@ SExt ::= SEQUENCE {
<marker id="CHOICE"></marker>
<title>CHOICE</title>
<p>The CHOICE type is a space saver and is similar to the concept of a
- 'union' in the C-language. As with the previous SET-type, the
- tags of all components of a CHOICE need to be distinct. If
- AUTOMATIC TAGS are defined for the module (which is
- preferable) the tags can be omitted completely in the ASN.1
- specification of a CHOICE.
- </p>
+ 'union' in the C language.</p>
<p>Assume:</p>
<pre>
+SomeModuleName DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
T ::= CHOICE {
- x [0] REAL,
- y [1] INTEGER,
- z [2] OBJECT IDENTIFIER }
- </pre>
+ x REAL,
+ y INTEGER,
+ z OBJECT IDENTIFIER }
+END </pre>
<p>It is then possible to assign values:</p>
<pre>
TVal1 = {y,17},
TVal2 = {z,{0,1,2}},
</pre>
- <p>A CHOICE value is always represented as the tuple
+ <p>A CHOICE value is always represented as the tuple
<c>{ChoiceAlternative, Val}</c> where <c>ChoiceAlternative</c>
- is an atom denoting the selected choice
- alternative.
- </p>
- <p>It is also allowed to have a CHOICE type tagged as follow:</p>
- <p></p>
- <pre>
-C ::= [PRIVATE 111] CHOICE {
- C1,
- C2 }
-
-C1 ::= CHOICE {
- a [0] INTEGER,
- b [1] BOOLEAN }
-
-C2 ::= CHOICE {
- c [2] INTEGER,
- d [3] OCTET STRING } </pre>
- <p>In this case, the top type C appears to have no tags at all in
- its components, however, both C1 and C2 are also defined as
- CHOICE types and they have distinct tags among themselves.
- Hence, the above type C is both legal and allowed.
+ is an atom denoting the selected choice alternative.
</p>
<section>
- <title>Extendable CHOICE</title>
+ <title>Extensible CHOICE</title>
<p>When a CHOICE contains an extension marker and the decoder detects
an unknown alternative of the CHOICE the value is represented as:</p>
<pre>
@@ -1192,26 +1113,29 @@ Arr2Val = ["abc",[14,34,54],"Octets"], </pre>
Where <c>Value</c> may be a value of yet another type T2.</p>
<p>For example:</p>
<pre>
+EmbeddedExample DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
B ::= SEQUENCE {
a Arr1,
- b [0] T }
+ b T }
Arr1 ::= SET SIZE (5) OF INTEGER (4..9)
T ::= CHOICE {
- x [0] REAL,
- y [1] INTEGER,
- z [2] OBJECT IDENTIFIER } </pre>
- <p>The above example can be assigned like this in Erlang:</p>
+ x REAL,
+ y INTEGER,
+ z OBJECT IDENTIFIER }
+ END </pre>
+ <p>The SEQUENCE b can be encoded like this in Erlang:</p>
<pre>
-V2 = #'B'{a=[4,5,6,7,8], b={x,7.77}}.
- </pre>
+1> 'EmbeddedExample':encode('B', {'B',[4,5,6,7,8],{x,"7.77"}}).
+{ok,&lt;&lt;5,56,0,8,3,55,55,55,46,69,45,50>>} </pre>
</section>
</section>
<section>
<title>Naming of Records in .hrl Files</title>
- <p>When an asn1 specification is compiled all defined types of
+ <p>When an ASN.1 specification is compiled all defined types of
type SET or SEQUENCE will result in a corresponding record in the
generated hrl file. This is because the values for SET/SEQUENCE
as mentioned in sections above are represented as records.</p>
@@ -1227,8 +1151,8 @@ V2 = #'B'{a=[4,5,6,7,8], b={x,7.77}}.
Emb ::= SEQUENCE {
a SEQUENCE OF OCTET STRING,
b SET {
- a [0] INTEGER,
- b [1] INTEGER DEFAULT 66},
+ a INTEGER,
+ b INTEGER DEFAULT 66},
c CHOICE {
a INTEGER,
b FooType } }
@@ -1299,7 +1223,7 @@ PType{T} ::= SEQUENCE{
<p>Types may refer to themselves. Suppose:</p>
<pre>
Rec ::= CHOICE {
- nothing [0] NULL,
+ nothing NULL,
something SEQUENCE {
a INTEGER,
b OCTET STRING,
@@ -1331,7 +1255,7 @@ tt TT ::= {a 77,b {"kalle","kula"}} </pre>
Firstly, it could be used as the value in some DEFAULT component:</p>
<pre>
SS ::= SET {
- s [0] OBJECT IDENTIFIER,
+ s OBJECT IDENTIFIER,
val TT DEFAULT tt } </pre>
<p>It could also be used from inside an Erlang program. If the above ASN.1
code was defined in ASN.1 module <c>Values</c>, then the ASN.1 value
@@ -1365,8 +1289,8 @@ SS ::= SET {
<marker id="Information Object"></marker>
<title>ASN.1 Information Objects (X.681)</title>
<p>Information Object Classes, Information Objects and Information
- Object Sets, (in the following called classes, objects and
- object sets respectively), are defined in the standard
+ Object Sets (in the following called classes, objects and
+ object sets respectively) are defined in the standard
definition [<cite id="X.681"></cite>]. In the following only a brief
explanation is given. </p>
<p>These constructs makes it possible to define open types,
@@ -1435,9 +1359,26 @@ StartMessage ::= SEQUENCE {
<p><c>StartMessage</c> can in the <c>content</c> field be
encoded with a value of any type that an object in the
<c>GENERAL-PROCEDURES</c> object set has in its <c>NEW MESSAGE</c> field. This field refers to a type field
- <c><![CDATA[&amp;Message]]></c> in the class. The <c>msgId</c> field is always
+ <c>&amp;Message</c> in the class. The <c>msgId</c> field is always
encoded as a PrintableString, since the field refers to a fixed type
in the class.</p>
+ <p>In practice, object sets are usually declared to be extensible so
+ so that more objects can be added to the set later. Extensibility is
+ indicated like this:</p>
+ <pre>
+GENERAL-PROCEDURES GENERAL-PROCEDURE ::= {
+ object1 | object2, ...} </pre>
+ <p>When decoding a type that uses an extensible set constraint,
+ there is always the possibility that the value in the UNIQUE
+ field is unknown (i.e. the type has been encoded with a later
+ version of the ASN.1 specification). When that happens, the
+ unencoded data will be returned wrapped in a tuple like this:</p>
+
+ <pre>
+{asn1_OPENTYPE,Binary}</pre>
+ <p>where <c>Binary</c> is an Erlang binary that contains the encoded
+ data. (If the option <c>legacy_erlang_types</c> has been given,
+ just the binary will be returned.)</p>
</section>
<section>
@@ -1466,132 +1407,11 @@ T1 ::= General{PrintableString}
T2 ::= General{BIT STRING}
</pre>
<p>An example of a value that can be encoded as type T1 is {12,"hello"}.</p>
- <p>Observe that the compiler not generates encode/decode functions for
- parameterized types, only for the instances of the parameterized
- types. So, if a file contains the types General{}, T1 and T2 above,
+ <p>Note that the compiler does not generate encode/decode functions for
+ parameterized types, but only for the instances of the parameterized
+ types. Therefore, if a file contains the types General{}, T1 and T2 above,
encode/decode functions will only be generated for T1 and T2.
</p>
</section>
-
- <section>
- <title>Tags</title>
- <p>Every built-in ASN.1 type, except CHOICE and ANY have a universal tag.
- This is a unique number that clearly identifies the type. <br></br>
-
- It is essential for all users of ASN.1 to
- understand all the details about tags.</p>
- <p>Tags are implicitly encoded in the BER encoding as shown below, but
- are hardly not accounted for in the PER encoding. In PER tags are
- used for instance to sort the components of a SET.</p>
- <p>There are four different types of tags.</p>
- <taglist>
- <tag><em>universal</em></tag>
- <item>
- <p>For types whose meaning is the same in all
- applications. Such as integers, sequences and so on; that is, all the built in
- types.</p>
- </item>
- <tag><em>application</em></tag>
- <item>
- <p>For application specific types for example, the types in
- X.400 Message handling service have this sort of tag.</p>
- </item>
- <tag><em>private</em></tag>
- <item>
- <p>For your own private types.</p>
- </item>
- <tag><em>context</em></tag>
- <item>
- <p>This is used to distinguish otherwise indistinguishable
- types in a specific context. For example, if we have two
- components of a
- CHOICE type that are both <c>INTEGER</c> values, there is no
- way for the decoder to
- decipher which component was actually chosen, since both
- components will be
- tagged as <c>INTEGER</c>. When this or similar situations occur,
- one or both of the components should be given a context specific
- to resolve the ambiguity.</p>
- </item>
- </taglist>
- <p>The tag in the case of the 'Apdu' type [PRIVATE 1] is encoded to a
- sequence of bytes making it possible for a
- decoder to look at the (initial) bytes that arrive and determine
- whether the rest of the bytes must be of the type associated
- with that particular sequence of bytes. This means that each
- tag must be uniquely associated with <em>only</em> one ASN.1
- type.
- </p>
- <p>Immediately following the tag is a sequence of bytes
- informing the decoder of the length of the instance. This is
- sometimes referred to as TLV (Tag length value) encoding.
- Hence, the structure of a BER encoded series of bytes is as shown in the table below.</p>
- <p></p>
- <table>
- <row>
- <cell align="left" valign="middle">Tag</cell>
- <cell align="left" valign="middle">Len</cell>
- <cell align="left" valign="middle">Value</cell>
- </row>
- <tcaption>Structure of a BER encoded series of bytes</tcaption>
- </table>
- </section>
-
- <section>
- <title>Encoding Rules</title>
- <p>When the first recommendation on ASN.1 was released 1988 it was
- accompanied with the Basic Encoding Rules, BER, as the only
- alternative for encoding.
- BER is a somewhat verbose protocol. It adopts a so-called TLV (type,
- length, value) approach to encoding in which every element of the
- encoding carries some type information, some length information and
- then the value of that element. Where the element is itself
- structured, then the Value part of the element is itself a series of
- embedded TLV components, to whatever depth is necessary. In summary,
- BER is not a compact encoding but is relatively fast and easy to
- produce.</p>
- <p>The DER (Distinguished Encoding Rule) encoding format was included in
- the standard in 1994. It is a specialized form of BER, which gives
- the encoder the option to encode some entities differently. For
- instance, is the value for TRUE any octet with any bit set to one. But,
- DER does not leave any such choices. The value for TRUE in the DER
- case is encoded as the octet <c>11111111</c>. So, the same value
- encoded by two different DER encoders must result in the same bit
- stream.</p>
- <p>A more compact encoding is achieved with the Packed Encoding
- Rules PER which was introduced together with the revised
- recommendation in 1994. PER takes a rather different approach from
- that taken by BER. The first difference is that the tag part in
- the TLV is omitted from the encodings, and any tags in the
- notation are not encoded. The potential ambiguities are resolved
- as follows:</p>
- <list type="bulleted">
- <item>
- <p>A CHOICE is encoded by first encoding a choice index which
- identifies the chosen
- alternative by its position in the notation.</p>
- </item>
- <item>
- <p>The elements of a SEQUENCE are transmitted in textual
- order. OPTIONAL or DEFAULT elements are preceded by a bit map
- to identify which elements are present. After sorting the
- elements of a SET in the "canonical tag order" as defined in
- X.680 8.6 they are treated as a SEQUENCE regarding OPTIONAL
- and DEFAULT elements. A SET is transferred in the sorted
- order.</p>
- </item>
- </list>
- <p>A second difference is that PER takes full account of the sub-typing
- information in that the encoded bytes are affected by the constraints.
- The BER encoded bytes are unaffected by the constraints.
- PER uses the sub-typing information to for example omit length fields
- whenever possible. </p>
- <p>The run-time functions, sometimes take the constraints into account
- both for BER and PER. For instance are SIZE constrained strings checked.</p>
- <p>There are two variants of PER, <em>aligned</em> and <em>unaligned</em>.
- In summary, PER results in compact encodings which require much more
- computation to produce than BER.
- </p>
- </section>
</chapter>
diff --git a/lib/asn1/doc/src/asn1ct.xml b/lib/asn1/doc/src/asn1ct.xml
index 4d5a1a402a..32ff2d52cf 100644
--- a/lib/asn1/doc/src/asn1ct.xml
+++ b/lib/asn1/doc/src/asn1ct.xml
@@ -45,10 +45,11 @@
<p>By default in OTP 17, the representation of the BIT STRING
and OCTET STRING types as Erlang terms have changed. BIT
STRING values are now Erlang bitstrings and OCTET STRING values
- are binaries. For details see <seealso
- marker="asn1_ug#BIT STRING">BIT STRING</seealso> and <seealso
- marker="asn1_ug#OCTET STRING">OCTET STRING</seealso> in User's
- Guide.</p>
+ are binaries. Also, an undecoded open type will now be wrapped in
+ a <c>asn1_OPENTYPE</c> tuple. For details see <seealso
+ marker="asn1_ug#BIT STRING">BIT STRING</seealso>, <seealso
+ marker="asn1_ug#OCTET STRING">OCTET STRING</seealso>, and
+ <seealso marker="asn1_ug#Information%20Object">ASN.1 Information Objects</seealso> in User's Guide.</p>
<p>To revert to the old representation of the types, use the
<c>legacy_erlang_types</c> option.</p>
</note>
diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml
index ff7962edd9..cb89bb298b 100644
--- a/lib/asn1/doc/src/notes.xml
+++ b/lib/asn1/doc/src/notes.xml
@@ -31,6 +31,112 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 3.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Subtyping an extensible ENUMERATED would cause an
+ compilation error. (Thanks to Morten Nygaard Ã…snes for
+ reporting this bug.)</p>
+ <p>
+ Own Id: OTP-11700</p>
+ </item>
+ <item>
+ <p>When specifying the value for an OCTET STRING in a
+ specification, the ASN.1 standard clearly states that the
+ value must be either a bstring or an hstring, but NOT a
+ cstring. The ASN.1 compiler will now generate a
+ compilation error if the value of an OCTET STRING is
+ given as a character string.</p>
+ <p>That is, the following example is now illegal:</p>
+ <p><c>string OCTET STRING ::= "Now illegal"</c></p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11727</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ By giving --enable-static-{nifs,drivers} to configure it
+ is now possible to statically linking of nifs and drivers
+ to the main Erlang VM binary. At the moment only the asn1
+ and crypto nifs of the Erlang/OTP nifs and drivers have
+ been prepared to be statically linked. For more details
+ see the Installation Guide in the System documentation.</p>
+ <p>
+ Own Id: OTP-11258</p>
+ </item>
+ <item>
+ <p>Code generation for the <c>per</c> and <c>uper</c>
+ backends has been somewhat improved.</p>
+ <p>
+ Own Id: OTP-11573</p>
+ </item>
+ <item>
+ <p>The OCTET STRING and BIT STRING types now have a more
+ natural mapping to Erlang types (binary and bitstring,
+ respectively), which is more efficient and will avoid
+ useless conversions between lists and
+ binaries/bitstrings.</p>
+ <p>This is an incompatible change. To revert to the old
+ mapping to support existing applications, use the
+ <c>legacy_erlang_types</c> option.</p>
+ <p>Impact: There is a potential for better performance,
+ as it is now possible to avoid conversions between lists
+ and binaries both in the generated ASN.1 encode/decode
+ code and in the application itself.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11594</p>
+ </item>
+ <item>
+ <p>All functions in the <c>asn1rt</c> module, as well as
+ <c>asn1ct:decode/3</c> and <c>asn1ct:encode/3</c>, are
+ now deprecated.</p>
+ <p>
+ Own Id: OTP-11731</p>
+ </item>
+ <item>
+ <p>
+ Generated .hrl files are now protected from being
+ included more than once.</p>
+ <p>
+ Own Id: OTP-11804</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 2.0.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/asn1/src/asn1.app.src b/lib/asn1/src/asn1.app.src
index f2ee8deb75..02cbba0f10 100644
--- a/lib/asn1/src/asn1.app.src
+++ b/lib/asn1/src/asn1.app.src
@@ -10,5 +10,6 @@
asn1db
]},
{env, []},
- {applications, [kernel, stdlib]}
+ {applications, [kernel, stdlib]},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index 9ec43197bf..8470e5a1b4 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -566,6 +566,8 @@ get_pos_of_def(#pobjectdef{pos=Pos}) ->
Pos;
get_pos_of_def(#pobjectsetdef{pos=Pos}) ->
Pos;
+get_pos_of_def(#'Externaltypereference'{pos=Pos}) ->
+ Pos;
get_pos_of_def(#'Externalvaluereference'{pos=Pos}) ->
Pos;
get_pos_of_def(_) ->
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index b9f2cb876a..e788aa5c6c 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -270,46 +270,30 @@ check_exports(S,Module = #module{}) ->
end
end.
-check_imports(S,Module = #module{ }) ->
- case Module#module.imports of
- {imports,[]} ->
- [];
- {imports,ImportList} when is_list(ImportList) ->
- check_imports2(S,ImportList,[]);
- _ ->
- []
- end.
-check_imports2(_S,[],Acc) ->
+check_imports(S, #module{imports={imports,Imports}}) ->
+ check_imports_1(S, Imports, []).
+
+check_imports_1(_S, [], Acc) ->
Acc;
-check_imports2(S,[#'SymbolsFromModule'{symbols=Imports,module=ModuleRef}|SFMs],Acc) ->
- NameOfDef =
- fun(#'Externaltypereference'{type=N}) -> N;
- (#'Externalvaluereference'{value=N}) -> N
- end,
- Module = NameOfDef(ModuleRef),
- Refs = [{M,R}||{{M,_},R} <- [{catch get_referenced_type(S,Ref),Ref}||Ref <- Imports]],
- {Illegal,Other} = lists:splitwith(fun({error,_}) -> true;(_) -> false end,
- Refs),
- ChainedRefs = [R||{M,R} <- Other, M =/= Module],
- IllegalRefs = [R||{error,R} <- Illegal] ++
- [R||{M,R} <- ChainedRefs,
- ok =/= chained_import(S,Module,M,NameOfDef(R))],
- ReportError =
- fun(Ref) ->
- NewS=S#state{type=Ref,tname=NameOfDef(Ref)},
- error({import,"imported undefined entity",NewS})
- end,
- check_imports2(S,SFMs,[ReportError(Err)||Err <- IllegalRefs]++Acc).
+check_imports_1(S, [#'SymbolsFromModule'{symbols=Imports,module=ModuleRef}|SFMs], Acc0) ->
+ Module = name_of_def(ModuleRef),
+ Refs0 = [{catch get_referenced_type(S, Ref),Ref} || Ref <- Imports],
+ Refs = [{M,R} || {{M,_},R} <- Refs0],
+ {Illegal,Other} = lists:splitwith(fun({error,_}) -> true;
+ (_) -> false
+ end, Refs),
+ ChainedRefs = [R || {M,R} <- Other, M =/= Module],
+ IllegalRefs = [R || {error,R} <- Illegal] ++
+ [R || {M,R} <- ChainedRefs,
+ ok =/= chained_import(S, Module, M, name_of_def(R))],
+ Acc = [return_asn1_error(S, Ref, {undefined_import,name_of_def(Ref),Module}) ||
+ Ref <- IllegalRefs] ++ Acc0,
+ check_imports_1(S, SFMs, Acc).
chained_import(S,ImpMod,DefMod,Name) ->
%% Name is a referenced structure that is not defined in ImpMod,
%% but must be present in the Imports list of ImpMod. The chain of
%% imports of Name must end in DefMod.
- NameOfDef =
- fun(#'Externaltypereference'{type=N}) -> N;
- (#'Externalvaluereference'{value=N}) -> N;
- (Other) -> Other
- end,
GetImports =
fun(_M_) ->
case asn1_db:dbget(_M_,'MODULE') of
@@ -321,9 +305,9 @@ chained_import(S,ImpMod,DefMod,Name) ->
FindNameInImports =
fun([],N,_) -> {no_mod,N};
([#'SymbolsFromModule'{symbols=Imports,module=ModuleRef}|SFMs],N,F) ->
- case [NameOfDef(X)||X <- Imports, NameOfDef(X) =:= N] of
+ case [name_of_def(X) || X <- Imports, name_of_def(X) =:= N] of
[] -> F(SFMs,N,F);
- [N] -> {NameOfDef(ModuleRef),N}
+ [N] -> {name_of_def(ModuleRef),N}
end
end,
case GetImports(ImpMod) of
@@ -1567,13 +1551,13 @@ check_defaultfields(S, Fields, ClassFields) ->
[] ->
ok;
[_|_]=Invalid ->
- throw(asn1_error(S, T, {invalid_fields,Invalid,Obj}))
+ asn1_error(S, T, {invalid_fields,Invalid,Obj})
end,
case ordsets:subtract(Mandatory, Present) of
[] ->
check_defaultfields_1(S, Fields, ClassFields, []);
[_|_]=Missing ->
- throw(asn1_error(S, T, {missing_mandatory_fields,Missing,Obj}))
+ asn1_error(S, T, {missing_mandatory_fields,Missing,Obj})
end.
check_defaultfields_1(_S, [], _ClassFields, Acc) ->
@@ -2614,7 +2598,7 @@ normalize_octetstring(S,Value,CType) ->
normalize_octetstring(S,String,CType);
_ ->
Item = S#state.value,
- throw(asn1_error(S, Item, illegal_octet_string_value))
+ asn1_error(S, Item, illegal_octet_string_value)
end.
normalize_objectidentifier(S, Value) ->
@@ -2645,7 +2629,7 @@ lookup_enum_value(S, Id, NNL) when is_atom(Id) ->
{_,_}=Ret ->
Ret;
false ->
- throw(asn1_error(S, S#state.value, {undefined,Id}))
+ asn1_error(S, S#state.value, {undefined,Id})
end.
normalize_choice(S,{'CHOICE',{C,V}},CType,NameList) when is_atom(C) ->
@@ -3084,7 +3068,7 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))};
{'INTEGER',NamedNumberList} ->
- TempNewDef#newt{type={'INTEGER',check_integer(S,NamedNumberList,Constr)},
+ TempNewDef#newt{type={'INTEGER',check_integer(S,NamedNumberList)},
tag=
merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))};
'REAL' ->
@@ -3092,8 +3076,7 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
TempNewDef#newt{tag=merge_tags(Tag,?TAG_PRIMITIVE(?N_REAL))};
{'BIT STRING',NamedNumberList} ->
- NewL = check_bitstring(S,NamedNumberList,Constr),
-%% erlang:display({asn1ct_check,NamedNumberList,NewL}),
+ NewL = check_bitstring(S, NamedNumberList),
TempNewDef#newt{type={'BIT STRING',NewL},
tag=
merge_tags(Tag,?TAG_PRIMITIVE(?N_BIT_STRING))};
@@ -4910,73 +4893,46 @@ imported1(Name,
end;
imported1(_Name,[]) ->
false.
-
-check_integer(_S,[],_C) ->
+%% Check the named number list for an INTEGER or a BIT STRING.
+check_named_number_list(_S, []) ->
[];
-check_integer(S,NamedNumberList,_C) ->
- case [X || X <- NamedNumberList, tuple_size(X) =:= 2] of
- NamedNumberList ->
- %% An already checked integer with NamedNumberList
- NamedNumberList;
- _ ->
- case check_unique(NamedNumberList,2) of
- [] ->
- check_int(S,NamedNumberList,[]);
- L when is_list(L) ->
- error({type,{duplicates,L},S}),
- unchanged
- end
+check_named_number_list(_S, [{_,_}|_]=NNL) ->
+ %% The named number list has already been checked.
+ NNL;
+check_named_number_list(S, NNL0) ->
+ %% Check that the names are unique.
+ T = S#state.type,
+ case check_unique(NNL0, 2) of
+ [] ->
+ NNL1 = [{Id,resolve_valueref(S, Val)} || {'NamedNumber',Id,Val} <- NNL0],
+ NNL = lists:keysort(2, NNL1),
+ case check_unique(NNL, 2) of
+ [] ->
+ NNL;
+ [Val|_] ->
+ asn1_error(S, T, {value_reused,Val})
+ end;
+ [H|_] ->
+ asn1_error(S, T, {namelist_redefinition,H})
end.
-
-check_int(S,[{'NamedNumber',Id,Num}|T],Acc) when is_integer(Num) ->
- check_int(S,T,[{Id,Num}|Acc]);
-check_int(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) ->
- Val = dbget_ex(S,S#state.mname,Name),
- check_int(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
-check_int(S,[{'NamedNumber',Id,{'Externalvaluereference',_,Mod,Name}}|T],Acc) ->
- Val = dbget_ex(S,Mod,Name),
- check_int(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
-check_int(_S,[],Acc) ->
- lists:keysort(2,Acc).
+resolve_valueref(S, #'Externalvaluereference'{module=Mod,value=Name}) ->
+ dbget_ex(S, Mod, Name);
+resolve_valueref(_, Val) when is_integer(Val) ->
+ Val.
-check_real(_S,_Constr) ->
- ok.
+check_integer(S, NNL) ->
+ check_named_number_list(S, NNL).
-check_bitstring(_S,[],_Constr) ->
- [];
-check_bitstring(S,NamedNumberList,_Constr) ->
- case check_unique(NamedNumberList,2) of
- [] ->
- check_bitstr(S,NamedNumberList,[]);
- L when is_list(L) ->
- error({type,{duplicates,L},S}),
- unchanged
- end.
+check_bitstring(S, NNL0) ->
+ NNL = check_named_number_list(S, NNL0),
+ _ = [asn1_error(S, S#state.type, {invalid_bit_number,Bit}) ||
+ {_,Bit} <- NNL, Bit < 0],
+ NNL.
-check_bitstr(S,[{'NamedNumber',Id,Num}|T],Acc)when is_integer(Num) ->
- check_bitstr(S,T,[{Id,Num}|Acc]);
-check_bitstr(S,[{'NamedNumber',Id,Name}|T],Acc) when is_atom(Name) ->
-%%check_bitstr(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) ->
-%% io:format("asn1ct_check:check_bitstr/3 hej hop ~w~n",[Name]),
- Val = dbget_ex(S,S#state.mname,Name),
-%% io:format("asn1ct_check:check_bitstr/3: ~w~n",[Val]),
- check_bitstr(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
-check_bitstr(S,[],Acc) ->
- case check_unique(Acc,2) of
- [] ->
- lists:keysort(2,Acc);
- L when is_list(L) ->
- error({type,{duplicate_values,L},S}),
- unchanged
- end;
-%% When a BIT STRING already is checked, for instance a COMPONENTS OF S
-%% where S is a sequence that has a component that is a checked BS, the
-%% NamedNumber list is a list of {atom(),integer()} elements.
-check_bitstr(S,[El={Id,Num}|Rest],Acc) when is_atom(Id),is_integer(Num) ->
- check_bitstr(S,Rest,[El|Acc]).
-
+check_real(_S,_Constr) ->
+ ok.
%% Check INSTANCE OF
%% check that DefinedObjectClass is of TYPE-IDENTIFIER class
@@ -4987,20 +4943,16 @@ check_instance_of(S,DefinedObjectClass,Constraint) ->
check_type_identifier(S,DefinedObjectClass),
iof_associated_type(S,Constraint).
-
-check_type_identifier(_S,'TYPE-IDENTIFIER') ->
- ok;
-check_type_identifier(S,Eref=#'Externaltypereference'{}) ->
- case get_referenced_type(S,Eref) of
- {_,#classdef{name='TYPE-IDENTIFIER'}} -> ok;
- {_,#classdef{typespec=NextEref}}
- when is_record(NextEref,'Externaltypereference') ->
- check_type_identifier(S,NextEref);
+check_type_identifier(S, Eref=#'Externaltypereference'{type=Class}) ->
+ case get_referenced_type(S, Eref) of
+ {_,#classdef{name='TYPE-IDENTIFIER'}} ->
+ ok;
+ {_,#classdef{typespec=#'Externaltypereference'{}=NextEref}} ->
+ check_type_identifier(S, NextEref);
{_,TD=#typedef{typespec=#type{def=#'Externaltypereference'{}}}} ->
- check_type_identifier(S,(TD#typedef.typespec)#type.def);
- Err ->
- error({type,{"object set in type INSTANCE OF "
- "not of class TYPE-IDENTIFIER",Eref,Err},S})
+ check_type_identifier(S, (TD#typedef.typespec)#type.def);
+ _ ->
+ asn1_error(S, S#state.type, {illegal_instance_of,Class})
end.
iof_associated_type(S,[]) ->
@@ -5130,9 +5082,6 @@ check_enumerated(S,NamedNumberList,_Constr) ->
%% the latter is returned if the ENUMERATION contains EXTENSIONMARK
check_enum(S,[{'NamedNumber',Id,Num}|T],Acc1,Acc2,Root) when is_integer(Num) ->
check_enum(S,T,[{Id,Num}|Acc1],Acc2,Root);
-check_enum(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc1,Acc2,Root) ->
- Val = dbget_ex(S,S#state.mname,Name),
- check_enum(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc1,Acc2,Root);
check_enum(S,['EXTENSIONMARK'|T],Acc1,Acc2,_Root) ->
NewAcc2 = lists:keysort(2,Acc1),
NewList = enum_number(lists:reverse(Acc2),NewAcc2,0,[],[]),
@@ -6748,7 +6697,7 @@ storeindb(#state{mname=Module}=S, [H|T], Errors) ->
storeindb(S, T, Errors);
Prev ->
PrevLine = asn1ct:get_pos_of_def(Prev),
- {error,Error} = asn1_error(S, H, {already_defined,Name,PrevLine}),
+ Error = return_asn1_error(S, H, {already_defined,Name,PrevLine}),
storeindb(S, T, [Error|Errors])
end;
storeindb(_, [], []) ->
@@ -6795,22 +6744,37 @@ findtypes_and_values([],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc) ->
{lists:reverse(Tacc),lists:reverse(Vacc),lists:reverse(Pacc),
lists:reverse(Cacc),lists:reverse(Oacc),lists:reverse(OSacc)}.
-asn1_error(#state{mname=Where}, Item, Error) ->
+return_asn1_error(#state{mname=Where}, Item, Error) ->
Pos = asn1ct:get_pos_of_def(Item),
- {error,{structured_error,{Where,Pos},?MODULE,Error}}.
+ {structured_error,{Where,Pos},?MODULE,Error}.
+
+asn1_error(S, Item, Error) ->
+ throw({error,return_asn1_error(S, Item, Error)}).
format_error({already_defined,Name,PrevLine}) ->
io_lib:format("the name ~p has already been defined at line ~p",
[Name,PrevLine]);
+format_error({illegal_instance_of,Class}) ->
+ io_lib:format("using INSTANCE OF on class '~s' is illegal, "
+ "because INSTANCE OF may only be used on the class TYPE-IDENTFIER",
+ [Class]);
format_error(illegal_octet_string_value) ->
"expecting a bstring or an hstring as value for an OCTET STRING";
format_error({invalid_fields,Fields,Obj}) ->
io_lib:format("invalid ~s in ~p", [format_fields(Fields),Obj]);
+format_error({invalid_bit_number,Bit}) ->
+ io_lib:format("the bit number '~p' is invalid", [Bit]);
format_error({missing_mandatory_fields,Fields,Obj}) ->
io_lib:format("missing mandatory ~s in ~p",
[format_fields(Fields),Obj]);
+format_error({namelist_redefinition,Name}) ->
+ io_lib:format("the name '~s' can not be redefined", [Name]);
format_error({undefined,Name}) ->
io_lib:format("'~s' is referenced, but is not defined", [Name]);
+format_error({undefined_import,Ref,Module}) ->
+ io_lib:format("'~s' is not exported from ~s", [Ref,Module]);
+format_error({value_reused,Val}) ->
+ io_lib:format("the value '~p' is used more than once", [Val]);
format_error(Other) ->
io_lib:format("~p", [Other]).
@@ -6826,14 +6790,6 @@ error({export,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) ->
Pos = Ref#'Externaltypereference'.pos,
io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Typename,Msg]),
{error,{export,Pos,Mname,Typename,Msg}};
-error({import,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) ->
- PosOfDef =
- fun(#'Externaltypereference'{pos=P}) -> P;
- (#'Externalvaluereference'{pos=P}) -> P
- end,
- Pos = PosOfDef(Ref),
- io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Typename,Msg]),
- {error,{import,Pos,Mname,Typename,Msg}};
% error({type,{Msg1,Msg2},#state{mname=Mname,type=Type,tname=Typename}})
% when is_record(Type,typedef) ->
% io:format("asn1error:~p:~p:~p ~p~n",
@@ -7134,3 +7090,6 @@ check_fold(S, [H|T], Check) ->
[Error|check_fold(S, T, Check)]
end;
check_fold(_, [], Check) when is_function(Check, 3) -> [].
+
+name_of_def(#'Externaltypereference'{type=N}) -> N;
+name_of_def(#'Externalvaluereference'{value=N}) -> N.
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index 4707e517b4..44b050e59d 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -1125,7 +1125,22 @@ pgen_info() ->
open_hrl(OutFile,Module) ->
File = lists:concat([OutFile,".hrl"]),
_ = open_output_file(File),
- gen_hrlhead(Module).
+ gen_hrlhead(Module),
+ Protector = hrl_protector(OutFile),
+ emit(["-ifndef(",Protector,").\n",
+ "-define(",Protector,", true).\n"
+ "\n"]).
+
+hrl_protector(OutFile) ->
+ BaseName = filename:basename(OutFile),
+ P = "_" ++ string:to_upper(BaseName) ++ "_HRL_",
+ [if
+ $A =< C, C =< $Z -> C;
+ $a =< C, C =< $a -> C;
+ $0 =< C, C =< $9 -> C;
+ true -> $_
+ end || C <- P].
+
%% EMIT functions ************************
%% ***************************************
@@ -1232,6 +1247,8 @@ pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) ->
0 ->
0;
Y ->
+ Protector = hrl_protector(get(outfile)),
+ emit(["-endif. %% ",Protector,"\n"]),
close_output_file(),
asn1ct:verbose("--~p--~n",
[{generated,lists:concat([get(outfile),".hrl"])}],
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index d438300596..782217ed2d 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -813,10 +813,10 @@ testExport(Config) ->
testImport(Config) ->
test(Config, fun testImport/3).
testImport(Config, Rule, Opts) ->
- {error, _} = asn1ct:compile(filename:join(?config(data_dir, Config),
- "ImportsFrom"),
- [Rule, {outdir, ?config(priv_dir, Config)}
- |Opts]).
+ Files = ["ImportsFrom","ImportsFrom2","ImportsFrom3"],
+ asn1_test_lib:compile_all(Files, Config, [Rule|Opts]),
+ 42 = 'ImportsFrom':i(),
+ ok.
testMegaco(Config) -> test(Config, fun testMegaco/3).
testMegaco(Config, Rule, Opts) ->
diff --git a/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1 b/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1
index 8c4f3a8f7e..b4ea943040 100644
--- a/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1
@@ -16,7 +16,9 @@ Names ::= SEQUENCE {
thirdName [2] INSTANCE OF OTHER-NAME ({TI})
}
-OTHER-NAME ::= TYPE-IDENTIFIER
+OTHER-NAME ::= YET-ANOTHER-NAME
+
+YET-ANOTHER-NAME ::= TYPE-IDENTIFIER
TI OTHER-NAME ::= {{INTEGER IDENTIFIED BY {2 4}} |
{Seq IDENTIFIED BY {2 3 4}} |
diff --git a/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1 b/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1
index 896a35d627..32b8f75dde 100644
--- a/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1
@@ -1,16 +1,8 @@
-ImportsFrom DEFINITIONS ::=
-
+ImportsFrom DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
-IMPORTS
-Type1, Type2, Type3
-FROM RemoteFile1 objid
-val1, val2, val3
-FROM RemoteFile2;
-
-objid OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) remote-operations(4) notation(0)}
-
-LocalType ::= INTEGER
+IMPORTS Int FROM ImportsFrom2;
+i Int ::= 42
END
diff --git a/lib/asn1/test/asn1_SUITE_data/ImportsFrom2.asn1 b/lib/asn1/test/asn1_SUITE_data/ImportsFrom2.asn1
new file mode 100644
index 0000000000..b0c29d24ae
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/ImportsFrom2.asn1
@@ -0,0 +1,7 @@
+ImportsFrom2 DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+IMPORTS Int FROM ImportsFrom3;
+
+LocalDef ::= OCTET STRING
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/ImportsFrom3.asn1 b/lib/asn1/test/asn1_SUITE_data/ImportsFrom3.asn1
new file mode 100644
index 0000000000..ca27b20697
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/ImportsFrom3.asn1
@@ -0,0 +1,4 @@
+ImportsFrom3 DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+ Int ::= INTEGER (0..63)
+END
diff --git a/lib/asn1/test/error_SUITE.erl b/lib/asn1/test/error_SUITE.erl
index 930b44cea6..8a0414708d 100644
--- a/lib/asn1/test/error_SUITE.erl
+++ b/lib/asn1/test/error_SUITE.erl
@@ -19,7 +19,8 @@
-module(error_SUITE).
-export([suite/0,all/0,groups/0,
- already_defined/1,enumerated/1,objects/1,values/1]).
+ already_defined/1,bitstrings/1,enumerated/1,
+ imports/1,instance_of/1,integers/1,objects/1,values/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -31,7 +32,11 @@ all() ->
groups() ->
[{p,parallel(),
[already_defined,
+ bitstrings,
enumerated,
+ imports,
+ instance_of,
+ integers,
objects,
values]}].
@@ -70,6 +75,23 @@ already_defined(Config) ->
} = run(P, Config),
ok.
+bitstrings(Config) ->
+ M = 'Bitstrings',
+ P = {M,
+ <<"Bitstrings DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ " Bs1 ::= BIT STRING {a(1), a(1)}\n"
+ " Bs2 ::= BIT STRING {a(1), b(2), a(3)}\n"
+ " Bs3 ::= BIT STRING {x(1), y(1)}\n"
+ " Bs4 ::= BIT STRING {x(-1), y(0)}\n"
+ "END\n">>},
+ {error,
+ [{structured_error,{M,2},asn1ct_check,{namelist_redefinition,a}},
+ {structured_error,{M,3},asn1ct_check,{namelist_redefinition,a}},
+ {structured_error,{M,4},asn1ct_check,{value_reused,1}},
+ {structured_error,{M,5},asn1ct_check,{invalid_bit_number,-1}}
+ ]} = run(P, Config),
+ ok.
+
enumerated(Config) ->
M = 'Enumerated',
P = {M,
@@ -98,6 +120,63 @@ enumerated(Config) ->
} = run(P, Config),
ok.
+imports(Config) ->
+ Ext = 'ExternalModule',
+ ExtP = {Ext,
+ <<"ExternalModule DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ "END\n">>},
+ ok = run(ExtP, Config),
+
+ M = 'Imports',
+ P = {M,
+ <<"Imports DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ "IMPORTS NotDefined FROM ExternalModule\n"
+ "X FROM UndefinedModule objid\n"
+ "Y, Z FROM UndefinedModule2;\n"
+ "objid OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) remote-operations(4)\n"
+ " notation(0)}\n"
+ "END\n">>},
+ {error,[{structured_error,{M,2},asn1ct_check,
+ {undefined_import,'NotDefined','ExternalModule'}},
+ {structured_error,{M,3},asn1ct_check,{undefined_import,'X','UndefinedModule'}},
+ {structured_error,{M,4},asn1ct_check,{undefined_import,'Y','UndefinedModule2'}},
+ {structured_error,{M,4},asn1ct_check,{undefined_import,'Z','UndefinedModule2'}}
+ ]} = run(P, Config),
+ ok.
+
+instance_of(Config) ->
+ M = 'InstanceOf',
+ P = {M,
+ <<"InstanceOf DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ "XX ::= INSTANCE OF CL ({TI})\n"
+ "CL ::= CLASS {\n"
+ "&id INTEGER,\n"
+ "&Type\n"
+ "}\n"
+ "o1 CL ::= {&id 1, &Type OCTET STRING}\n"
+ "TI CL ::= { o1 }\n"
+ "END\n">>},
+ {error,
+ [{structured_error,{M,2},asn1ct_check,{illegal_instance_of,'CL'}}
+ ]} = run(P, Config),
+ ok.
+
+integers(Config) ->
+ M = 'Integers',
+ P = {M,
+ <<"Integers DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ " Int1 ::= INTEGER {a(1), a(1)}\n"
+ " Int2 ::= INTEGER {a(1), b(2), a(3)}\n"
+ " Int3 ::= INTEGER {x(1), y(1)}\n"
+ "END\n">>},
+ {error,
+ [{structured_error,{M,2},asn1ct_check,{namelist_redefinition,a}},
+ {structured_error,{M,3},asn1ct_check,{namelist_redefinition,a}},
+ {structured_error,{M,4},asn1ct_check,{value_reused,1}}
+ ]} = run(P, Config),
+ ok.
+
+
objects(Config) ->
M = 'Objects',
P = {M,
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index 153c64ebdd..1f16f31f6b 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1,2 +1,2 @@
#next version number to use is 2.0
-ASN1_VSN = 2.0.4
+ASN1_VSN = 3.0
diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml
index 859ff9df14..cab6dfea51 100644
--- a/lib/common_test/doc/src/ct_hooks.xml
+++ b/lib/common_test/doc/src/ct_hooks.xml
@@ -450,12 +450,15 @@
</func>
<func>
- <name>Module:on_tc_fail(TestcaseName, Reason, CTHState) -&gt;
+ <name>Module:on_tc_fail(TestName, Reason, CTHState) -&gt;
NewCTHState</name>
<fsummary>Called after the CTH scope ends</fsummary>
<type>
- <v>TestcaseName = init_per_suite | end_per_suite |
- init_per_group | end_per_group | 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>
<v>Reason = term()</v>
<v>CTHState = NewCTHState = term()</v>
</type>
@@ -463,14 +466,16 @@
<desc>
<p> OPTIONAL </p>
- <p>This function is called whenever a testcase fails.
- It is called after the post function has been called for
- the testcase which failed. i.e.
- if init_per_suite fails this function is called after
+ <p>This function is called whenever a test case (or config function)
+ fails. It is called after the post function has been called for
+ the failed test case. I.e. if init_per_suite fails, this function
+ is called after
<seealso marker="#Module:post_init_per_suite-4">
- post_init_per_suite</seealso>, and if a testcase fails it is called
+ post_init_per_suite</seealso>, and if a test case fails, it is called
after <seealso marker="#Module:post_end_per_testcase-4">
- post_end_per_testcase</seealso>.</p>
+ post_end_per_testcase</seealso>. If the failed test case belongs
+ to a test case group, the first argument is a tuple
+ <c>{FuncName,GroupName}</c>, otherwise simply the function name.</p>
<p>The data which comes with the Reason follows the same format as the
<seealso marker="event_handler_chapter#failreason">FailReason
@@ -481,12 +486,14 @@
</func>
<func>
- <name>Module:on_tc_skip(TestcaseName, Reason, CTHState) -&gt;
+ <name>Module:on_tc_skip(TestName, Reason, CTHState) -&gt;
NewCTHState</name>
<fsummary>Called after the CTH scope ends</fsummary>
<type>
- <v>TestcaseName = end_per_suite | {init_per_group,GroupName} |
- {end_per_group,GroupName} | 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>
<v>Reason = {tc_auto_skip | tc_user_skip, term()}</v>
<v>CTHState = NewCTHState = term()</v>
@@ -495,14 +502,17 @@
<desc>
<p> OPTIONAL </p>
- <p>This function is called whenever a testcase is skipped.
- It is called after the post function has been called for the
- testcase which was skipped.
- i.e. if init_per_group is skipped this function is called after
- <seealso marker="#Module:post_init_per_suite-4">post_init_per_group
- </seealso>, and if a testcase is skipped it is called after
- <seealso marker="#Module:post_end_per_testcase-4">post_end_per_testcase
- </seealso>.</p>
+ <p>This function is called whenever a test case (or config function)
+ is skipped. It is called after the post function has been called
+ for the skipped test case. I.e. if init_per_group is skipped, this
+ function is called after
+ <seealso marker="#Module:post_init_per_group-4">
+ post_init_per_group</seealso>, and if a test case is skipped,
+ it is called after
+ <seealso marker="#Module:post_end_per_testcase-4">
+ post_end_per_testcase</seealso>. If the skipped test case belongs to a
+ test case group, the first argument is a tuple <c>{FuncName,GroupName}</c>,
+ otherwise simply the function name.</p>
<p>The data which comes with the Reason follows the same format as
<seealso marker="event_handler_chapter#tc_auto_skip">tc_auto_skip
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index 39259b092a..d8e79ca80e 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -108,6 +108,7 @@
EvHandler2 InitArg2 and .. EvHandlerN InitArgN]
[-include InclDir1 InclDir2 .. InclDirN]
[-no_auto_compile]
+ [-abort_if_missing_suites]
[-muliply_timetraps Multiplier]
[-scale_timetraps]
[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
@@ -144,6 +145,7 @@
EvHandler2 InitArg2 and .. EvHandlerN InitArgN]
[-include InclDir1 InclDir2 .. InclDirN]
[-no_auto_compile]
+ [-abort_if_missing_suites]
[-muliply_timetraps Multiplier]
[-scale_timetraps]
[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
@@ -171,6 +173,7 @@
[-decrypt_key Key] | [-decrypt_file KeyFile]
[-include InclDir1 InclDir2 .. InclDirN]
[-no_auto_compile]
+ [-abort_if_missing_suites]
[-muliply_timetraps Multiplier]
[-scale_timetraps]
[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml
index 47d0ba59fb..45f01c12ec 100644
--- a/lib/common_test/doc/src/event_handler_chapter.xml
+++ b/lib/common_test/doc/src/event_handler_chapter.xml
@@ -227,11 +227,13 @@
<item>
<marker id="tc_auto_skip"></marker>
- <c>#event{name = tc_auto_skip, data = {Suite,Func,Reason}}</c>
+ <c>#event{name = tc_auto_skip, data = {Suite,TestName,Reason}}</c>
<p><c>Suite = atom()</c>, the name of the suite.</p>
- <p><c>Func = atom() | {end_per_group,GroupName}</c>, the name of the test case
- or configuration function.</p>
- <p><c>GroupName = atom()</c>, name of the group.</p>
+ <p><c>TestName = init_per_suite | end_per_suite |
+ {init_per_group,GroupName} | {end_per_group,GroupName} |
+ {FuncName,GroupName} | FuncName</c></p>
+ <p><c>FuncName = atom()</c>, the name of the test case or configuration function.</p>
+ <p><c>GroupName = atom()</c>, the name of the test case group.</p>
<p><c>Reason = {failed,FailReason} |
{require_failed_in_suite0,RequireInfo}</c>,
reason for auto skipping <c>Func</c>.</p>
@@ -252,21 +254,26 @@
<c>init_per_group</c>, a failed <c>require</c> in <c>suite/0</c>, or a failed test case
in a sequence. Note that this event is never received as a result of a test case getting
skipped because of <c>init_per_testcase</c> failing, since that information is carried with
- the <c>tc_done</c> event.
+ the <c>tc_done</c> event. If a failed test case belongs to a test case group, the second
+ data element is a tuple <c>{FuncName,GroupName}</c>, otherwise simply the function name.
</p></item>
-
+
<item>
<marker id="tc_user_skip"></marker>
- <c>#event{name = tc_user_skip, data = {Suite,Func,Comment}}</c>
+ <c>#event{name = tc_user_skip, data = {Suite,TestName,Comment}}</c>
<p><c>Suite = atom()</c>, the name of the suite.</p>
- <p><c>Func = atom() | {end_per_group,GroupName}</c>, the name of the test case
- or configuration function.</p>
- <p><c>GroupName = atom()</c>, name of the group.</p>
+ <p><c>TestName = init_per_suite | end_per_suite |
+ {init_per_group,GroupName} | {end_per_group,GroupName} |
+ {FuncName,GroupName} | FuncName</c></p>
+ <p><c>FuncName = atom()</c>, the name of the test case or configuration function.</p>
+ <p><c>GroupName = atom()</c>, the name of the test case group.</p>
<p><c>Comment = string()</c>, reason for skipping the test case.</p>
<p>This event specifies that a test case has been skipped by the user.
It is only ever received if the skip was declared in a test specification.
Otherwise, user skip information is received as a <c>{skipped,SkipReason}</c>
- result in the <c>tc_done</c> event for the test case.
+ result in the <c>tc_done</c> event for the test case. If a skipped test case belongs
+ to a test case group, the second data element is a tuple <c>{FuncName,GroupName}</c>,
+ otherwise simply the function name.
</p></item>
<item><c>#event{name = test_stats, data = {Ok,Failed,Skipped}}</c>
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index f10d5f85bf..ddfeb0964b 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -32,6 +32,184 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The error generated if a test case process received an
+ exit from a linked process while executing
+ init_per_testcase/2, was handled incorrectly by Common
+ Test. The problem has been solved, and Common Test now
+ reports this type of error correctly, with proper error
+ reason and exit location as well.</p>
+ <p>
+ Own Id: OTP-11643</p>
+ </item>
+ <item>
+ <p>
+ Running a parallel test case group with two or more
+ instances of the same test case would result in identical
+ log file names, and one test case instance would
+ overwrite the log file of another. This problem has been
+ solved.</p>
+ <p>
+ Own Id: OTP-11644</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ <item>
+ <p>
+ The <c>cth_surefire</c> hook would crash in
+ <c>pre_init_per_suite/3</c> if a previous hook returned
+ <c>{skip,Reason}</c> or <c>{fail,Reason}</c> instead of a
+ <c>Config</c> list. This error has been corrected, and
+ <c>cth_surefire</c> will now simply propagate the
+ received <c>InitData</c> value instead.</p>
+ <p>
+ Own Id: OTP-11811</p>
+ </item>
+ <item>
+ <p>
+ Specs of return values are corrected for
+ <c>ct_netconfc:get/2,3</c>,
+ <c>ct_netconfc:get_config/3,4</c>,
+ <c>ct_netconfc:action/2,3</c>,
+ <c>ct_netconfc:send_rpc/2,3</c> and
+ <c>ct_netconfc:send/2,3</c>.</p>
+ <p>
+ Own Id: OTP-11834 Aux Id: seq12574 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ ct_telnet can now log all communication taking place
+ during a telnet session. Previously, only information
+ about ct_telnet operations and commands, as well as
+ explicitly requested data from the server, was logged.</p>
+ <p>
+ Furthermore, a logging mechanism based on an Error Logger
+ event handler and a dedicated Common Test hook,
+ <c>cth_conn_log</c>, now makes it possible to print data
+ for individual connections to separate log files. Please
+ see the <c>ct_telnet</c> reference manual for more
+ information and examples.</p>
+ <p>
+ Important note: A new argument, <c>ConnName</c> has been
+ added to the <c>unix_telnet:connect/5</c> callback
+ function. This forces users that use private ct_telnet
+ callback modules to update their code according to
+ <c>unix_telnet:connect/6</c>. Please see the
+ <c>unix_telnet</c> reference manual and source code
+ module for details.</p>
+ <p>
+ Own Id: OTP-11440 Aux Id: seq12457 </p>
+ </item>
+ <item>
+ <p>
+ A new timeout option has been introduced for the
+ <c>ct_telnet:expect/3</c> function. With
+ <c>{total_timeout,Time}</c> it's possible to set a time
+ limit for the complete expect operation. After
+ <c>Time</c> milliseconds, <c>expect/3</c> returns
+ <c>{error,timeout}</c>. The default value used if
+ <c>total_timeout</c> is not specified, is infinity (i.e.
+ no time limit). Please see the <c>ct_telnet</c> reference
+ manual for more information.</p>
+ <p>
+ Own Id: OTP-11689</p>
+ </item>
+ <item>
+ <p>
+ Some function specs are corrected or moved and some edoc
+ comments are corrected in order to allow use of edoc.
+ (Thanks to Pierre Fenoll)</p>
+ <p>
+ Own Id: OTP-11702</p>
+ </item>
+ <item>
+ <p>
+ Test case group name information has been added to the
+ data sent with <c>tc_user_skip</c> and
+ <c>tc_auto_skip</c> event messages, as well as the data
+ passed in calls to the CT Hook functions
+ <c>on_tc_skip/3</c> and <c>on_tc_fail/3</c>. The
+ modification only affects the function name
+ element/argument. This value remains an atom if the test
+ case in question does not belong to a test case group.
+ Otherwise a tuple <c>{FuncName,GroupName}</c>
+ (<c>{atom(),atom()}</c>) is passed instead.</p>
+ <p>
+ Note that this change may (depending on the patterns used
+ for matching) require modifications of user event
+ handlers and hook modules. Please see the Event Handling
+ chapter in the Common Test User's Guide, and the
+ reference manual for <c>ct_hooks</c>, for details.</p>
+ <p>
+ Note also that the Test Server framework callback
+ function <c>report/2</c> has been modified. This change
+ only affects users with test frameworks interfacing Test
+ Server rather than Common Test. See the
+ <c>test_server_ctrl</c> reference manual for details.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11732 Aux Id: seq12541 </p>
+ </item>
+ <item>
+ <p>
+ If Common Test can't prompt the user to abort or continue
+ the test run when one or more test suites fail to
+ compile, a new option,
+ <c>{abort_if_missing_suites,Bool}</c>, can be used to
+ specify whether it should proceed with the test run, or
+ stop execution. The default value of <c>Bool</c> is
+ <c>false</c> (i.e. to proceed even if suites are
+ missing).</p>
+ <p>
+ Own Id: OTP-11769</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ common_test: Fix problems reported by Dialyzer.</p>
+ <p>
+ Own Id: OTP-11525</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.7.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index a4a77ee400..864f82cb63 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -59,7 +59,15 @@
<p>If compilation should fail for one or more suites, the compilation errors
are printed to tty and the operator is asked if the test run should proceed
without the missing suites, or be aborted. If the operator chooses to proceed,
- it is noted in the HTML log which tests have missing suites.</p>
+ it is noted in the HTML log which tests have missing suites. If Common Test is
+ unable to prompt the user after compilation failure (if Common Test doesn't
+ control stdin), the test run will proceed automatically without the missing
+ suites. This behaviour can however be modified with the
+ <c><![CDATA[ct_run]]></c> flag <c><![CDATA[-abort_if_missing_suites]]></c>,
+ or the <c><![CDATA[ct:run_test/1]]></c> option
+ <c><![CDATA[{abort_if_missing_suites,TrueOrFalse}]]></c>. If
+ <c><![CDATA[abort_if_missing_suites]]></c> is set (to true), the test run
+ will stop immediately if some suites fail to compile.</p>
<p>Any help module (i.e. regular Erlang module with name not ending with
"_SUITE") that resides in the same test object directory as a suite
@@ -167,6 +175,7 @@
<seealso marker="ct_hooks_chapter#builtin_cths">Built-in Common Test Hooks</seealso>. Default is <c>true</c>.</item>
<item><c><![CDATA[-include]]></c>, specifies include directories (see above).</item>
<item><c><![CDATA[-no_auto_compile]]></c>, disables the automatic test suite compilation feature (see above).</item>
+ <item><c><![CDATA[-abort_if_missing_suites]]></c>, aborts the test run if one or more suites fail to compile (see above).</item>
<item><c><![CDATA[-multiply_timetraps <n>]]></c>, extends <seealso marker="write_test_chapter#timetraps">timetrap
timeout</seealso> values.</item>
<item><c><![CDATA[-scale_timetraps <bool>]]></c>, enables automatic <seealso marker="write_test_chapter#timetraps">timetrap
@@ -589,8 +598,8 @@
Common Test will either execute one test run per specification file, or
join the files and perform all tests within one single test run. The first
behaviour is the default one. The latter requires that the start
- flag/option <c>join_suites</c> is provided, e.g.
- <c>run_test -spec ./my_tests1.ts ./my_tests2.ts -join_suites</c>.</p>
+ flag/option <c>join_specs</c> is provided, e.g.
+ <c>run_test -spec ./my_tests1.ts ./my_tests2.ts -join_specs</c>.</p>
<p>Joining a number of specifications, or running them separately, can
also be accomplished with (and may be combined with) test specification
@@ -744,6 +753,9 @@
{auto_compile, Bool},
{auto_compile, NodeRefs, Bool},
+ {abort_if_missing_suites, Bool},
+ {abort_if_missing_suites, NodeRefs, Bool},
+
{config, ConfigFiles}.
{config, ConfigDir, ConfigBaseNames}.
{config, NodeRefs, ConfigFiles}.
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index 18c1dec784..e28751fb59 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -62,5 +62,10 @@
ct_master,
ct_master_logs]},
{applications, [kernel,stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies,["xmerl-1.3.7","webtool-0.8.10","tools-2.6.14",
+ "test_server-3.7","stdlib-2.0","ssh-3.0.1",
+ "snmp-4.25.1","sasl-2.4","runtime_tools-1.8.14",
+ "kernel-3.0","inets-5.10","erts-6.0",
+ "debugger-4.0","crypto-3.3","compiler-5.0"]}]}.
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index e6732f7fc7..85afdc7834 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -150,7 +150,8 @@ run(TestDirs) ->
%%% {silent_connections,Conns} | {stylesheet,CSSFile} |
%%% {cover,CoverSpecFile} | {cover_stop,Bool} | {step,StepOpts} |
%%% {event_handler,EventHandlers} | {include,InclDirs} |
-%%% {auto_compile,Bool} | {create_priv_dir,CreatePrivDir} |
+%%% {auto_compile,Bool} | {abort_if_missing_suites,Bool} |
+%%% {create_priv_dir,CreatePrivDir} |
%%% {multiply_timetraps,M} | {scale_timetraps,Bool} |
%%% {repeat,N} | {duration,DurTime} | {until,StopTime} |
%%% {force_stop,ForceStop} | {decrypt,DecryptKeyOrFile} |
@@ -772,7 +773,7 @@ comment(Format, Args) when is_list(Format), is_list(Args) ->
send_html_comment(Comment) ->
Html = "<font color=\"green\">" ++ Comment ++ "</font>",
- ct_util:set_testdata({comment,Html}),
+ ct_util:set_testdata({{comment,group_leader()},Html}),
test_server:comment(Html).
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl
index d733df27dc..cff02a46d9 100644
--- a/lib/common_test/src/ct_conn_log_h.erl
+++ b/lib/common_test/src/ct_conn_log_h.erl
@@ -204,13 +204,8 @@ pretty_head({{{Y,Mo,D},{H,Mi,S}},MicroS},ConnMod,Text0) ->
micro2milli(MicroS)]).
pretty_title(#conn_log{client=Client}=Info) ->
- case actionstr(Info) of
- {no_server,Action} ->
- io_lib:format("= Client ~w ~s ",[Client,Action]);
- Action ->
- io_lib:format("= Client ~w ~s ~ts ",[Client,Action,
- serverstr(Info)])
- end.
+ io_lib:format("= Client ~w ~s ~ts ",
+ [Client,actionstr(Info),serverstr(Info)]).
actionstr(#conn_log{action=send}) -> "----->";
actionstr(#conn_log{action=cmd}) -> "----->";
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 580588fbd2..20903607dc 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -249,8 +249,8 @@ init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) ->
end
end.
-ct_suite_init(Suite, Func, PostInitHook, Config) when is_list(Config) ->
- case ct_hooks:init_tc(Suite, Func, Config) of
+ct_suite_init(Suite, FuncSpec, PostInitHook, Config) when is_list(Config) ->
+ case ct_hooks:init_tc(Suite, FuncSpec, Config) of
NewConfig when is_list(NewConfig) ->
PostInitHookResult = do_post_init_hook(PostInitHook, NewConfig),
{ok, [PostInitHookResult ++ NewConfig]};
@@ -657,13 +657,21 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) ->
_ ->
ok
end,
- ct_util:delete_testdata(comment),
+ if Func == end_per_group; Func == end_per_suite ->
+ %% clean up any saved comments
+ ct_util:match_delete_testdata({comment,'_'});
+ true ->
+ %% attemp to delete any saved comment for this TC
+ case process_info(TCPid, group_leader) of
+ {group_leader,TCGL} ->
+ ct_util:delete_testdata({comment,TCGL});
+ _ ->
+ ok
+ end
+ end,
ct_util:delete_suite_data(last_saved_config),
- FuncSpec = case group_or_func(Func,Args) of
- {_,_GroupName,_} = Group -> Group;
- _ -> Func
- end,
+ FuncSpec = group_or_func(Func,Args),
{Result1,FinalNotify} =
case ct_hooks:end_tc(
@@ -853,7 +861,7 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
_ ->
%% this notification comes from the test case process, so
%% we can add error info to comment with test_server:comment/1
- case ct_util:get_testdata(comment) of
+ case ct_util:get_testdata({comment,group_leader()}) of
undefined ->
test_server:comment(ErrorHtml);
Comment ->
@@ -1277,28 +1285,35 @@ report(What,Data) ->
ct_util:set_testdata({What,Data}),
ok;
tc_start ->
- %% Data = {{Suite,Func},LogFileName}
+ %% Data = {{Suite,{Func,GroupName}},LogFileName}
+ Data1 = case Data of
+ {{Suite,{Func,undefined}},LFN} -> {{Suite,Func},LFN};
+ _ -> Data
+ end,
ct_event:sync_notify(#event{name=tc_logfile,
node=node(),
- data=Data}),
+ data=Data1}),
ok;
tc_done ->
- {_Suite,Case,Result} = Data,
+ {Suite,{Func,GrName},Result} = Data,
+ Data1 = if GrName == undefined -> {Suite,Func,Result};
+ true -> Data
+ end,
case Result of
{failed, _} ->
- ct_hooks:on_tc_fail(What, Data);
+ ct_hooks:on_tc_fail(What, Data1);
{skipped,{failed,{_,init_per_testcase,_}}} ->
- ct_hooks:on_tc_skip(tc_auto_skip, Data);
+ ct_hooks:on_tc_skip(tc_auto_skip, Data1);
{skipped,{require_failed,_}} ->
- ct_hooks:on_tc_skip(tc_auto_skip, Data);
+ ct_hooks:on_tc_skip(tc_auto_skip, Data1);
{skipped,_} ->
- ct_hooks:on_tc_skip(tc_user_skip, Data);
+ ct_hooks:on_tc_skip(tc_user_skip, Data1);
{auto_skipped,_} ->
- ct_hooks:on_tc_skip(tc_auto_skip, Data);
+ ct_hooks:on_tc_skip(tc_auto_skip, Data1);
_Else ->
ok
end,
- case {Case,Result} of
+ case {Func,Result} of
{init_per_suite,_} ->
ok;
{end_per_suite,_} ->
@@ -1327,20 +1342,17 @@ report(What,Data) ->
tc_user_skip ->
%% test case or config function specified as skipped in testspec,
%% or init config func for suite/group has returned {skip,Reason}
- %% Data = {Suite,Case,Comment} |
- %% {Suite,{GroupConfigFunc,GroupName},Comment}
+ %% Data = {Suite,{Func,GroupName},Comment}
{Func,Data1} = case Data of
- {Suite,{ConfigFunc,undefined},Cmt} ->
- {ConfigFunc,{Suite,ConfigFunc,Cmt}};
- {_,{ConfigFunc,_},_} -> {ConfigFunc,Data};
- {_,Case,_} -> {Case,Data}
+ {Suite,{F,undefined},Comment} ->
+ {F,{Suite,F,Comment}};
+ D = {_,{F,_},_} ->
+ {F,D}
end,
-
ct_event:sync_notify(#event{name=tc_user_skip,
node=node(),
data=Data1}),
ct_hooks:on_tc_skip(What, Data1),
-
if Func /= init_per_suite, Func /= init_per_group,
Func /= end_per_suite, Func /= end_per_group ->
add_to_stats(user_skipped);
@@ -1350,13 +1362,12 @@ report(What,Data) ->
tc_auto_skip ->
%% test case skipped because of error in config function, or
%% config function skipped because of error in info function
- %% Data = {Suite,Case,Comment} |
- %% {Suite,{GroupConfigFunc,GroupName},Comment}
+ %% Data = {Suite,{Func,GroupName},Comment}
{Func,Data1} = case Data of
- {Suite,{ConfigFunc,undefined},Cmt} ->
- {ConfigFunc,{Suite,ConfigFunc,Cmt}};
- {_,{ConfigFunc,_},_} -> {ConfigFunc,Data};
- {_,Case,_} -> {Case,Data}
+ {Suite,{F,undefined},Comment} ->
+ {F,{Suite,F,Comment}};
+ D = {_,{F,_},_} ->
+ {F,D}
end,
%% this test case does not have a log, so printouts
%% from event handlers should end up in the main log
@@ -1364,7 +1375,6 @@ report(What,Data) ->
node=node(),
data=Data1}),
ct_hooks:on_tc_skip(What, Data1),
-
if Func /= end_per_suite,
Func /= end_per_group ->
add_to_stats(auto_skipped);
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index 078d6b1a44..56082086f6 100644
--- a/lib/common_test/src/ct_gen_conn.erl
+++ b/lib/common_test/src/ct_gen_conn.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -307,7 +307,8 @@ call(Pid, Msg, Timeout) ->
end.
return({To,Ref},Result) ->
- To ! {Ref, Result}.
+ To ! {Ref, Result},
+ ok.
init_gen(Parent,Opts) ->
process_flag(trap_exit,true),
@@ -344,7 +345,7 @@ loop(Opts) ->
link(NewPid),
put(conn_pid,NewPid),
loop(Opts#gen_opts{conn_pid=NewPid,
- cb_state=NewState});
+ cb_state=NewState});
Error ->
ct_util:unregister_connection(self()),
log("Reconnect failed. Giving up!",
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index e845e9e908..df4c98d9d1 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -64,11 +64,16 @@ terminate(Hooks) ->
%% @doc Called as each test case is started. This includes all configuration
%% tests.
--spec init_tc(Mod :: atom(), Func :: atom(), Args :: list()) ->
+-spec init_tc(Mod :: atom(),
+ FuncSpec :: atom() |
+ {ConfigFunc :: init_per_group | end_per_group,
+ GroupName :: atom(),
+ Properties :: list()},
+ Args :: list()) ->
NewConfig :: proplists:proplist() |
- {skip, Reason :: term()} |
- {auto_skip, Reason :: term()} |
- {fail, Reason :: term()}.
+ {skip, Reason :: term()} |
+ {auto_skip, Reason :: term()} |
+ {fail, Reason :: term()}.
init_tc(Mod, init_per_suite, Config) ->
Info = try proplists:get_value(ct_hooks, Mod:suite(),[]) of
@@ -82,8 +87,8 @@ init_tc(Mod, init_per_suite, Config) ->
call(fun call_generic/3, Config ++ Info, [pre_init_per_suite, Mod]);
init_tc(Mod, end_per_suite, Config) ->
call(fun call_generic/3, Config, [pre_end_per_suite, Mod]);
-init_tc(Mod, {init_per_group, GroupName, Opts}, Config) ->
- maybe_start_locker(Mod, GroupName, Opts),
+init_tc(Mod, {init_per_group, GroupName, Properties}, Config) ->
+ maybe_start_locker(Mod, GroupName, Properties),
call(fun call_generic/3, Config, [pre_init_per_group, GroupName]);
init_tc(_Mod, {end_per_group, GroupName, _}, Config) ->
call(fun call_generic/3, Config, [pre_end_per_group, GroupName]);
@@ -93,15 +98,18 @@ init_tc(_Mod, TC, Config) ->
%% @doc Called as each test case is completed. This includes all configuration
%% tests.
-spec end_tc(Mod :: atom(),
- Func :: atom(),
+ FuncSpec :: atom() |
+ {ConfigFunc :: init_per_group | end_per_group,
+ GroupName :: atom(),
+ Properties :: list()},
Args :: list(),
Result :: term(),
- Resturn :: term()) ->
+ Return :: term()) ->
NewConfig :: proplists:proplist() |
- {skip, Reason :: term()} |
- {auto_skip, Reason :: term()} |
- {fail, Reason :: term()} |
- ok | '$ct_no_change'.
+ {skip, Reason :: term()} |
+ {auto_skip, Reason :: term()} |
+ {fail, Reason :: term()} |
+ ok | '$ct_no_change'.
end_tc(Mod, init_per_suite, Config, _Result, Return) ->
call(fun call_generic/3, Return, [post_init_per_suite, Mod, Config],
@@ -112,18 +120,20 @@ end_tc(Mod, end_per_suite, Config, Result, _Return) ->
end_tc(_Mod, {init_per_group, GroupName, _}, Config, _Result, Return) ->
call(fun call_generic/3, Return, [post_init_per_group, GroupName, Config],
'$ct_no_change');
-end_tc(Mod, {end_per_group, GroupName, Opts}, Config, Result, _Return) ->
+end_tc(Mod, {end_per_group, GroupName, Properties}, Config, Result, _Return) ->
Res = call(fun call_generic/3, Result,
[post_end_per_group, GroupName, Config], '$ct_no_change'),
- maybe_stop_locker(Mod, GroupName,Opts),
+ maybe_stop_locker(Mod, GroupName, Properties),
Res;
end_tc(_Mod, TC, Config, Result, _Return) ->
call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config],
'$ct_no_change').
+%% Case = TestCase | {TestCase,GroupName}
on_tc_skip(How, {Suite, Case, Reason}) ->
call(fun call_cleanup/3, {How, Reason}, [on_tc_skip, Suite, Case]).
+%% Case = TestCase | {TestCase,GroupName}
on_tc_fail(_How, {Suite, Case, Reason}) ->
call(fun call_cleanup/3, Reason, [on_tc_fail, Suite, Case]).
diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl
index 35920ec1dc..a3861dc745 100644
--- a/lib/common_test/src/ct_netconfc.erl
+++ b/lib/common_test/src/ct_netconfc.erl
@@ -1,7 +1,7 @@
%%----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -536,7 +536,7 @@ send(Client, SimpleXml) ->
Client :: client(),
SimpleXml :: simple_xml(),
Timeout :: timeout(),
- Result :: ok | {error,error_reason()}.
+ Result :: simple_xml() | {error,error_reason()}.
%% @doc Send an XML document to the server.
%%
%% The given XML document is sent as is to the server. This function
@@ -556,7 +556,7 @@ send_rpc(Client, SimpleXml) ->
Client :: client(),
SimpleXml :: simple_xml(),
Timeout :: timeout(),
- Result :: ok | {error,error_reason()}.
+ Result :: [simple_xml()] | {error,error_reason()}.
%% @doc Send a Netconf <code>rpc</code> request to the server.
%%
%% The given XML document is wrapped in a valid Netconf
@@ -635,7 +635,7 @@ get(Client, Filter) ->
Client :: client(),
Filter :: simple_xml() | xpath(),
Timeout :: timeout(),
- Result :: {ok,simple_xml()} | {error,error_reason()}.
+ Result :: {ok,[simple_xml()]} | {error,error_reason()}.
%% @doc Get data.
%%
%% This operation returns both configuration and state data from the
@@ -661,7 +661,7 @@ get_config(Client, Source, Filter) ->
Source :: netconf_db(),
Filter :: simple_xml() | xpath(),
Timeout :: timeout(),
- Result :: {ok,simple_xml()} | {error,error_reason()}.
+ Result :: {ok,[simple_xml()]} | {error,error_reason()}.
%% @doc Get configuration data.
%%
%% To be able to access another source than `running', the server
@@ -759,7 +759,7 @@ action(Client,Action) ->
Client :: client(),
Action :: simple_xml(),
Timeout :: timeout(),
- Result :: {ok,simple_xml()} | {error,error_reason()}.
+ Result :: {ok,[simple_xml()]} | {error,error_reason()}.
%% @doc Execute an action.
%%
%% @end
@@ -1334,7 +1334,7 @@ handle_data(NewData,#state{connection=Connection,buff=Buff} = State) ->
%% first answer
P=#pending{tref=TRef,caller=Caller} =
lists:last(Pending),
- timer:cancel(TRef),
+ _ = timer:cancel(TRef),
Reason1 = {failed_to_parse_received_data,Reason},
ct_gen_conn:return(Caller,{error,Reason1}),
lists:delete(P,Pending)
@@ -1454,7 +1454,7 @@ decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) ->
{noreply,State#state{hello_status = {error,Reason}}}
end;
#pending{tref=TRef,caller=Caller} ->
- timer:cancel(TRef),
+ _ = timer:cancel(TRef),
case decode_hello(E) of
{ok,SessionId,Capabilities} ->
ct_gen_conn:return(Caller,ok),
@@ -1482,7 +1482,7 @@ decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) ->
case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of
[#pending{tref=TRef,
caller=Caller}] ->
- timer:cancel(TRef),
+ _ = timer:cancel(TRef),
ct_gen_conn:return(Caller,E),
{noreply,State#state{pending=[]}};
_ ->
@@ -1504,7 +1504,7 @@ get_msg_id(Attrs) ->
decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) ->
case lists:keytake(MsgId,#pending.msg_id,Pending) of
{value, #pending{tref=TRef,op=Op,caller=Caller}, Pending1} ->
- timer:cancel(TRef),
+ _ = timer:cancel(TRef),
Content = forward_xmlns_attr(Attrs,Content0),
{CallerReply,{ServerReply,State2}} =
do_decode_rpc_reply(Op,Content,State#state{pending=Pending1}),
@@ -1519,7 +1519,7 @@ decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) ->
msg_id=undefined,
op=undefined,
caller=Caller}] ->
- timer:cancel(TRef),
+ _ = timer:cancel(TRef),
ct_gen_conn:return(Caller,E),
{noreply,State#state{pending=[]}};
_ ->
@@ -1862,10 +1862,7 @@ ssh_open(#options{host=Host,timeout=Timeout,port=Port,ssh=SshOpts,name=Name}) ->
end;
{error, Reason} ->
ssh:close(CM),
- {error,{ssh,could_not_open_channel,Reason}};
- Other ->
- %% Bug in ssh?? got {closed,0} here once...
- {error,{ssh,unexpected_from_session_channel,Other}}
+ {error,{ssh,could_not_open_channel,Reason}}
end;
{error,Reason} ->
{error,{ssh,could_not_connect_to_server,Reason}}
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 7c797be03e..03cf06abed 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -71,6 +71,7 @@
enable_builtin_hooks,
include = [],
auto_compile,
+ abort_if_missing_suites,
silent_connections = [],
stylesheet,
multiply_timetraps = 1,
@@ -246,9 +247,11 @@ script_start1(Parent, Args) ->
Vts = get_start_opt(vts, true, Args),
Shell = get_start_opt(shell, true, Args),
Cover = get_start_opt(cover, fun([CoverFile]) -> ?abs(CoverFile) end, Args),
- CoverStop = get_start_opt(cover_stop, fun([CS]) -> list_to_atom(CS) end, Args),
+ CoverStop = get_start_opt(cover_stop,
+ fun([CS]) -> list_to_atom(CS) end, Args),
LogDir = get_start_opt(logdir, fun([LogD]) -> LogD end, Args),
- LogOpts = get_start_opt(logopts, fun(Os) -> [list_to_atom(O) || O <- Os] end,
+ LogOpts = get_start_opt(logopts,
+ fun(Os) -> [list_to_atom(O) || O <- Os] end,
[], Args),
Verbosity = verbosity_args2opts(Args),
MultTT = get_start_opt(multiply_timetraps,
@@ -311,6 +314,12 @@ script_start1(Parent, Args) ->
application:set_env(common_test, auto_compile, false),
{false,[]}
end,
+
+ %% abort test run if some suites can't be compiled
+ AbortIfMissing = get_start_opt(abort_if_missing_suites,
+ fun([]) -> true;
+ ([Bool]) -> list_to_atom(Bool)
+ end, false, Args),
%% silent connections
SilentConns =
get_start_opt(silent_connections,
@@ -347,6 +356,7 @@ script_start1(Parent, Args) ->
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
auto_compile = AutoCompile,
+ abort_if_missing_suites = AbortIfMissing,
include = IncludeDirs,
silent_connections = SilentConns,
stylesheet = Stylesheet,
@@ -551,6 +561,9 @@ combine_test_opts(TS, Specs, Opts) ->
ACBool
end,
+ AbortIfMissing = choose_val(Opts#opts.abort_if_missing_suites,
+ TSOpts#opts.abort_if_missing_suites),
+
BasicHtml =
case choose_val(Opts#opts.basic_html,
TSOpts#opts.basic_html) of
@@ -578,6 +591,7 @@ combine_test_opts(TS, Specs, Opts) ->
enable_builtin_hooks = EnableBuiltinHooks,
stylesheet = Stylesheet,
auto_compile = AutoCompile,
+ abort_if_missing_suites = AbortIfMissing,
include = AllInclude,
multiply_timetraps = MultTT,
scale_timetraps = ScaleTT,
@@ -753,6 +767,7 @@ script_usage() ->
"\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
+ "\n\t[-abort_if_missing_suites]"
"\n\t[-multiply_timetraps N]"
"\n\t[-scale_timetraps]"
"\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
@@ -775,6 +790,7 @@ script_usage() ->
"\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
+ "\n\t[-abort_if_missing_suites]"
"\n\t[-multiply_timetraps N]"
"\n\t[-scale_timetraps]"
"\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
@@ -799,6 +815,7 @@ script_usage() ->
"\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
+ "\n\t[-abort_if_missing_suites]"
"\n\t[-multiply_timetraps N]"
"\n\t[-scale_timetraps]"
"\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
@@ -1026,6 +1043,10 @@ run_test2(StartOpts) ->
{ACBool,[]}
end,
+ %% abort test run if some suites can't be compiled
+ AbortIfMissing = get_start_opt(abort_if_missing_suites, value, false,
+ StartOpts),
+
%% decrypt config file
case proplists:get_value(decrypt, StartOpts) of
undefined ->
@@ -1067,6 +1088,7 @@ run_test2(StartOpts) ->
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
auto_compile = AutoCompile,
+ abort_if_missing_suites = AbortIfMissing,
include = Include,
silent_connections = SilentConns,
stylesheet = Stylesheet,
@@ -1401,6 +1423,7 @@ get_data_for_node(#testspec{label = Labels,
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
auto_compile = ACs,
+ abort_if_missing_suites = AiMSs,
include = Incl,
multiply_timetraps = MTs,
scale_timetraps = STs,
@@ -1435,6 +1458,7 @@ get_data_for_node(#testspec{label = Labels,
EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node],
FiltCTHooks = [Hook || {N,Hook} <- CTHooks, N==Node],
AutoCompile = proplists:get_value(Node, ACs),
+ AbortIfMissing = proplists:get_value(Node, AiMSs),
Include = [I || {N,I} <- Incl, N==Node],
#opts{label = Label,
profile = Profile,
@@ -1451,6 +1475,7 @@ get_data_for_node(#testspec{label = Labels,
ct_hooks = FiltCTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
auto_compile = AutoCompile,
+ abort_if_missing_suites = AbortIfMissing,
include = Include,
multiply_timetraps = MT,
scale_timetraps = ST,
@@ -1722,8 +1747,8 @@ compile_and_run(Tests, Skip, Opts, Args) ->
{SuiteErrs,HelpErrs} = auto_compile(TestSuites),
{TestSuites,SuiteErrs,SuiteErrs++HelpErrs}
end,
-
- case continue(AllMakeErrors) of
+
+ case continue(AllMakeErrors, Opts#opts.abort_if_missing_suites) of
true ->
SavedErrors = save_make_errors(SuiteMakeErrors),
ct_repeat:log_loop_info(Args),
@@ -2047,9 +2072,9 @@ final_skip([Skip|Skips], Final) ->
final_skip([], Final) ->
lists:reverse(Final).
-continue([]) ->
+continue([], _) ->
true;
-continue(_MakeErrors) ->
+continue(_MakeErrors, AbortIfMissingSuites) ->
io:nl(),
OldGl = group_leader(),
case set_group_leader_same_as_shell() of
@@ -2077,26 +2102,26 @@ continue(_MakeErrors) ->
true
end;
false -> % no shell process to use
- true
+ not AbortIfMissingSuites
end.
set_group_leader_same_as_shell() ->
%%! Locate the shell process... UGLY!!!
GS2or3 = fun(P) ->
- case process_info(P,initial_call) of
- {initial_call,{group,server,X}} when X == 2 ; X == 3 ->
- true;
- _ ->
- false
- end
- end,
+ case process_info(P,initial_call) of
+ {initial_call,{group,server,X}} when X == 2 ; X == 3 ->
+ true;
+ _ ->
+ false
+ end
+ end,
case [P || P <- processes(), GS2or3(P),
- true == lists:keymember(shell,1,
- element(2,process_info(P,dictionary)))] of
- [GL|_] ->
- group_leader(GL, self());
- [] ->
- false
+ true == lists:keymember(shell,1,
+ element(2,process_info(P,dictionary)))] of
+ [GL|_] ->
+ group_leader(GL, self());
+ [] ->
+ false
end.
check_and_add([{TestDir0,M,_} | Tests], Added, PA) ->
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index b4d82a53cf..3b2652d06c 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -46,100 +46,80 @@
%%
%% == Logging ==
%%
-%% `ct_telnet' can be configured to uses the `error_logger' for logging telnet
-%% traffic. A special purpose error handler is implemented in
-%% `ct_conn_log_h'. To use this error handler, add the `cth_conn_log'
-%% hook in your test suite, e.g:
-%%
+%% The default logging behaviour of `ct_telnet' is to print information
+%% to the test case HTML log about performed operations and commands
+%% and their corresponding results. What won't be printed to the HTML log
+%% are text strings sent from the telnet server that are not explicitly
+%% received by means of a `ct_telnet' function such as `expect/3'.
+%% `ct_telnet' may however be configured to use a special purpose event handler,
+%% implemented in `ct_conn_log_h', for logging <b>all</b> telnet traffic.
+%% To use this handler, you need to install a Common Test hook named
+%% `cth_conn_log'. Example (using the test suite info function):
%%
%% ```
%% suite() ->
-%% [{ct_hooks, [{cth_conn_log, [{conn_mod(),hook_options()}]}]}].
-%%'''
+%% [{ct_hooks, [{cth_conn_log, [{conn_mod(),hook_options()}]}]}].
+%% '''
%%
%% `conn_mod()' is the name of the common_test module implementing
%% the connection protocol, i.e. `ct_telnet'.
%%
-%% The hook option `log_type' specifies the type of logging:
-%%
-%% <dl>
-%% <dt>`raw'</dt>
-%% <dd>The sent and received telnet data is logged to a separate
-%% text file as is, without any formatting. A link to the file is
-%% added to the test case HTML log.</dd>
+%% The `cth_conn_log' hook performs unformatted logging of telnet data to
+%% a separate text file. All telnet communication is captured and printed,
+%% including arbitrary data sent from the server. The link to this text file
+%% can be found on the top of the test case HTML log.
%%
-%% <dt>`html (default)'</dt>
-%% <dd>The sent and received telnet traffic is pretty printed
-%% directly in the test case HTML log.</dd>
-%%
-%% <dt>`silent'</dt>
-%% <dd>Telnet traffic is not logged.</dd>
-%% </dl>
-%%
-%% By default, all telnet traffic is logged in one single log
-%% file. However, it is possible to have different connections logged
-%% in separate files. To do this, use the hook option `hosts' and
-%% list the names of the servers/connections that will be used in the
-%% suite. Note that the connections must be named for this to work
+%% By default, data for all telnet connections is logged in one common
+%% file (named `default'), which might get messy e.g. if multiple telnet
+%% sessions are running in parallel. It is therefore possible to create a
+%% separate log file for each connection. To configure this, use the hook
+%% option `hosts' and list the names of the servers/connections that will be
+%% used in the suite. Note that the connections must be named for this to work
%% (see the `open' function below).
%%
-%% The `hosts' option has no effect if `log_type' is set to `html' or
-%% `silent'.
-%%
-%% The hook options can also be specified in a configuration file with
-%% the configuration variable `ct_conn_log':
+%% The hook option named `log_type' may be used to change the `cth_conn_log'
+%% behaviour. The default value of this option is `raw', which results in the
+%% behaviour described above. If the value is set to `html', all telnet
+%% communication is printed to the test case HTML log instead.
%%
-%% ```
-%% {ct_conn_log,[{conn_mod(),hook_options()}]}.
-%% '''
-%%
-%% For example:
+%% All `cth_conn_log' hook options described above can also be specified in
+%% a configuration file with the configuration variable `ct_conn_log'. Example:
%%
%% ```
-%% {ct_conn_log,[{ct_telnet,[{log_type,raw},
-%% {hosts,[key_or_name()]}]}]}
+%% {ct_conn_log, [{ct_telnet,[{log_type,raw},
+%% {hosts,[key_or_name()]}]}]}
%% '''
%%
%% <b>Note</b> that hook options specified in a configuration file
-%% will overwrite any hardcoded hook options in the test suite.
+%% will overwrite any hardcoded hook options in the test suite!
%%
-%% === Logging example 1 ===
+%% === Logging example ===
%%
-%% The following `ct_hooks' statement will cause raw printing of
-%% telnet traffic to separate logs for the connections named
-%% `server1' and `server2'. Any other connections will be logged
-%% to default telnet log.
+%% The following `ct_hooks' statement will cause printing of telnet traffic
+%% to separate logs for the connections named `server1' and `server2'.
+%% Traffic for any other connections will be logged in the default telnet log.
%%
%% ```
%% suite() ->
-%% [{ct_hooks, [{cth_conn_log, [{ct_telnet,[{log_type,raw}},
-%% {hosts,[server1,server2]}]}
-%% ]}]}].
+%% [{ct_hooks,
+%% [{cth_conn_log, [{ct_telnet,[{hosts,[server1,server2]}]}]}]}].
%%'''
%%
-%% === Logging example 2 ===
-%%
-%% The following configuration file will cause raw logging of all
-%% telnet traffic into one single text file.
+%% As previously explained, the above specification could also be provided
+%% by means of an entry like this in a configuration file:
%%
%% ```
-%% {ct_conn_log,[{ct_telnet,[{log_type,raw}]}]}.
+%% {ct_conn_log, [{ct_telnet,[{hosts,[server1,server2]}]}]}.
%% '''
%%
-%% The `ct_hooks' statement must look like this:
+%% in which case the `ct_hooks' statement in the test suite may simply look
+%% like this:
%%
%% ```
%% suite() ->
-%% [{ct_hooks, [{cth_conn_log, []}]}].
+%% [{ct_hooks, [{cth_conn_log, []}]}].
%% '''
%%
-%% The same `ct_hooks' statement without the configuration file would
-%% cause HTML logging of all telnet connections into the test case
-%% HTML log.
-%%
-%% <b>Note</b> that if the `cth_conn_log' hook is not added, telnet
-%% traffic is still logged in the test case HTML log file (on the legacy
-%% `ct_telnet' format).
%% @end
%% @type connection_type() = telnet | ts1 | ts2
@@ -205,6 +185,7 @@ open(Name) ->
%%% Name = target_name()
%%% ConnType = ct_telnet:connection_type()
%%% Handle = ct_telnet:handle()
+%%% Reason = term()
%%%
%%% @doc Open a telnet connection to the specified target host.
open(Name,ConnType) ->
@@ -234,6 +215,7 @@ open(KeyOrName,ConnType,TargetMod) ->
%%% TargetMod = atom()
%%% Extra = term()
%%% Handle = handle()
+%%% Reason = term()
%%%
%%% @doc Open a telnet connection to the specified target host.
%%%
@@ -281,13 +263,22 @@ open(KeyOrName,ConnType,TargetMod,Extra) ->
end,
log(undefined,open,"Connecting to ~p(~p)",
[KeyOrName,Addr1]),
- ct_gen_conn:start(KeyOrName,full_addr(Addr1,ConnType),
- {TargetMod,KeepAlive,Extra},?MODULE)
+ Reconnect =
+ case ct:get_config({telnet_settings,reconnection_attempts}) of
+ 0 -> false;
+ _ -> true
+ end,
+ ct_gen_conn:start(full_addr(Addr1,ConnType),
+ {TargetMod,KeepAlive,Extra},
+ ?MODULE, [{name,KeyOrName},
+ {reconnect,Reconnect},
+ {old,true}])
end.
%%%-----------------------------------------------------------------
%%% @spec close(Connection) -> ok | {error,Reason}
-%%% Connection = ct_telnet:connection()
+%%% Connection = ct_telnet:connection()
+%%% Reason = term()
%%%
%%% @doc Close the telnet connection and stop the process managing it.
%%%
@@ -300,7 +291,7 @@ close(Connection) ->
{ok,Pid} ->
log(undefined,close,"Connection closed, handle: ~w",[Pid]),
case ct_gen_conn:stop(Pid) of
- {error,{process_down,Pid,noproc}} ->
+ {error,{process_down,Pid,_}} ->
{error,already_closed};
Result ->
Result
@@ -322,6 +313,7 @@ cmd(Connection,Cmd) ->
%%% Cmd = string()
%%% Timeout = integer()
%%% Data = [string()]
+%%% Reason = term()
%%% @doc Send a command via telnet and wait for prompt.
cmd(Connection,Cmd,Timeout) ->
case get_handle(Connection) of
@@ -342,6 +334,7 @@ cmdf(Connection,CmdFormat,Args) ->
%%% Args = list()
%%% Timeout = integer()
%%% Data = [string()]
+%%% Reason = term()
%%% @doc Send a telnet command and wait for prompt
%%% (uses a format string and list of arguments to build the command).
cmdf(Connection,CmdFormat,Args,Timeout) when is_list(Args) ->
@@ -352,6 +345,7 @@ cmdf(Connection,CmdFormat,Args,Timeout) when is_list(Args) ->
%%% @spec get_data(Connection) -> {ok,Data} | {error,Reason}
%%% Connection = ct_telnet:connection()
%%% Data = [string()]
+%%% Reason = term()
%%% @doc Get all data which has been received by the telnet client
%%% since last command was sent.
get_data(Connection) ->
@@ -366,6 +360,7 @@ get_data(Connection) ->
%%% @spec send(Connection,Cmd) -> ok | {error,Reason}
%%% Connection = ct_telnet:connection()
%%% Cmd = string()
+%%% Reason = term()
%%% @doc Send a telnet command and return immediately.
%%%
%%% <p>The resulting output from the command can be read with
@@ -383,6 +378,7 @@ send(Connection,Cmd) ->
%%% Connection = ct_telnet:connection()
%%% CmdFormat = string()
%%% Args = list()
+%%% Reason = term()
%%% @doc Send a telnet command and return immediately (uses a format
%%% string and a list of arguments to build the command).
sendf(Connection,CmdFormat,Args) when is_list(Args) ->
@@ -405,9 +401,11 @@ expect(Connection,Patterns) ->
%%% Prompt = string()
%%% Tag = term()
%%% Opts = [Opt]
-%%% Opt = {timeout,Timeout} | repeat | {repeat,N} | sequence |
-%%% {halt,HaltPatterns} | ignore_prompt | no_prompt_check
-%%% Timeout = integer()
+%%% Opt = {idle_timeout,IdleTimeout} | {total_timeout,TotalTimeout} |
+%%% repeat | {repeat,N} | sequence | {halt,HaltPatterns} |
+%%% ignore_prompt | no_prompt_check
+%%% IdleTimeout = infinity | integer()
+%%% TotalTimeout = infinity | integer()
%%% N = integer()
%%% HaltPatterns = Patterns
%%% MatchList = [Match]
@@ -433,11 +431,16 @@ expect(Connection,Patterns) ->
%%% will also include the matched <code>Tag</code>. Else, only
%%% <code>RxMatch</code> is returned.</p>
%%%
-%%% <p>The <code>timeout</code> option indicates that the function
+%%% <p>The <code>idle_timeout</code> option indicates that the function
%%% shall return if the telnet client is idle (i.e. if no data is
-%%% received) for more than <code>Timeout</code> milliseconds. Default
+%%% received) for more than <code>IdleTimeout</code> milliseconds. Default
%%% timeout is 10 seconds.</p>
%%%
+%%% <p>The <code>total_timeout</code> option sets a time limit for
+%%% the complete expect operation. After <code>TotalTimeout</code>
+%%% milliseconds, <code>{error,timeout}</code> is returned. The default
+%%% value is <code>infinity</code> (i.e. no time limit).</p>
+%%%
%%% <p>The function will always return when a prompt is found, unless
%%% any of the <code>ignore_prompt</code> or
%%% <code>no_prompt_check</code> options are used, in which case it
@@ -570,14 +573,14 @@ handle_msg({cmd,Cmd,Timeout},State) ->
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,2000}]);
+ [{idle_timeout,2000}]);
{ip,false} ->
silent_teln_expect(State#state.name,
State#state.teln_pid,
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,200}]);
+ [{idle_timeout,200}]);
{ip,true} ->
ok
end,
@@ -601,11 +604,12 @@ handle_msg({cmd,Cmd,Timeout},State) ->
end_gen_log(),
{Return,State#state{buffer=NewBuffer,prompt=Prompt}};
handle_msg({send,Cmd},State) ->
- log(State,send,"Cmd: ~p",[Cmd]),
-
+ start_gen_log(heading(send,State#state.name)),
+ log(State,send,"Sending: ~p",[Cmd]),
+
debug_cont_gen_log("Throwing Buffer:",[]),
debug_log_lines(State#state.buffer),
-
+
case {State#state.type,State#state.prompt} of
{ts,_} ->
silent_teln_expect(State#state.name,
@@ -613,18 +617,19 @@ handle_msg({send,Cmd},State) ->
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,2000}]);
+ [{idle_timeout,2000}]);
{ip,false} ->
silent_teln_expect(State#state.name,
State#state.teln_pid,
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,200}]);
+ [{idle_timeout,200}]);
{ip,true} ->
ok
end,
ct_telnet_client:send_data(State#state.teln_pid,Cmd),
+ end_gen_log(),
{ok,State#state{buffer=[],prompt=false}};
handle_msg(get_data,State) ->
start_gen_log(heading(get_data,State#state.name)),
@@ -752,8 +757,8 @@ check_if_prompt_was_reached(Data,_) when is_list(Data) ->
check_if_prompt_was_reached(_,_) ->
false.
-%%% @hidden
-%% Functions for logging ct_telnet reports and telnet data
+%%%-----------------------------------------------------------------
+%%% Functions for logging ct_telnet reports and telnet data
heading(Action,undefined) ->
io_lib:format("~w ~w",[?MODULE,Action]);
@@ -763,6 +768,8 @@ heading(Action,Name) ->
force_log(State,Action,String,Args) ->
log(State,Action,String,Args,true).
+%%%-----------------------------------------------------------------
+%%% @hidden
log(State,Action,String,Args) when is_record(State, state) ->
log(State,Action,String,Args,false);
log(Name,Action,String,Args) when is_atom(Name) ->
@@ -770,6 +777,8 @@ log(Name,Action,String,Args) when is_atom(Name) ->
log(TelnPid,Action,String,Args) when is_pid(TelnPid) ->
log(#state{teln_pid=TelnPid},Action,String,Args,false).
+%%%-----------------------------------------------------------------
+%%% @hidden
log(undefined,String,Args) ->
log(#state{},undefined,String,Args,false);
log(Name,String,Args) when is_atom(Name) ->
@@ -777,72 +786,73 @@ log(Name,String,Args) when is_atom(Name) ->
log(TelnPid,String,Args) when is_pid(TelnPid) ->
log(#state{teln_pid=TelnPid},undefined,String,Args).
+%%%-----------------------------------------------------------------
+%%% @hidden
log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
Action,String,Args,ForcePrint) ->
Name1 = if Name == undefined -> get({ct_telnet_pid2name,TelnPid});
true -> Name
end,
Silent = get(silent),
- case ct_util:get_testdata({cth_conn_log,?MODULE}) of
- Result when Result /= undefined, Result /= silent, Silent /= true ->
- {PrintHeader,PreBR} = if Action==undefined ->
- {false,""};
- true ->
- {true,"\n"}
- end,
- error_logger:info_report(#conn_log{header=PrintHeader,
- client=self(),
- conn_pid=TelnPid,
- address={Host,Port},
- name=Name1,
- action=Action,
- module=?MODULE},
- {PreBR++String,Args});
- Result when Result /= undefined ->
- ok;
- _ when Action == open; Action == close; Action == reconnect;
- Action == info; Action == error ->
- ct_gen_conn:log(heading(Action,Name1),String,Args);
- _ when ForcePrint == false ->
- case ct_util:is_silenced(telnet) of
- true ->
- ok;
- false ->
- ct_gen_conn:cont_log(String,Args)
+
+ if Action == general_io ->
+ case ct_util:get_testdata({cth_conn_log,?MODULE}) of
+ HookMode when HookMode /= undefined, HookMode /= silent,
+ Silent /= true ->
+ error_logger:info_report(#conn_log{header=false,
+ client=self(),
+ conn_pid=TelnPid,
+ address={Host,Port},
+ name=Name1,
+ action=Action,
+ module=?MODULE},
+ {String,Args});
+ _ -> %% hook inactive or silence requested
+ ok
end;
- _ when ForcePrint == true ->
- case ct_util:is_silenced(telnet) of
- true ->
- %% call log/3 now instead of cont_log/2 since
- %% start_gen_log/1 will not have been previously called
+
+ true ->
+ if Action == open; Action == close; Action == reconnect;
+ Action == info; Action == error ->
ct_gen_conn:log(heading(Action,Name1),String,Args);
- false ->
- ct_gen_conn:cont_log(String,Args)
+
+ ForcePrint == false ->
+ case ct_util:is_silenced(telnet) of
+ true ->
+ ok;
+ false ->
+ ct_gen_conn:cont_log(String,Args)
+ end;
+
+ ForcePrint == true ->
+ case ct_util:is_silenced(telnet) of
+ true ->
+ %% call log/3 now instead of cont_log/2 since
+ %% start_gen_log/1 will not have been previously
+ %% called
+ ct_gen_conn:log(heading(Action,Name1),String,Args);
+ false ->
+ ct_gen_conn:cont_log(String,Args)
+ end
end
end.
+%%%-----------------------------------------------------------------
+%%% @hidden
start_gen_log(Heading) ->
- case ct_util:get_testdata({cth_conn_log,?MODULE}) of
- undefined ->
- %% check if output is suppressed
- case ct_util:is_silenced(telnet) of
- true -> ok;
- false -> ct_gen_conn:start_log(Heading)
- end;
- _ ->
- ok
+ %% check if output is suppressed
+ case ct_util:is_silenced(telnet) of
+ true -> ok;
+ false -> ct_gen_conn:start_log(Heading)
end.
+%%%-----------------------------------------------------------------
+%%% @hidden
end_gen_log() ->
- case ct_util:get_testdata({cth_conn_log,?MODULE}) of
- undefined ->
- %% check if output is suppressed
- case ct_util:is_silenced(telnet) of
- true -> ok;
- false -> ct_gen_conn:end_log()
- end;
- _ ->
- ok
+ %% check if output is suppressed
+ case ct_util:is_silenced(telnet) of
+ true -> ok;
+ false -> ct_gen_conn:end_log()
end.
%%% @hidden
@@ -863,14 +873,13 @@ teln_cmd(Pid,Cmd,Prx,Timeout) ->
teln_receive_until_prompt(Pid,Prx,Timeout).
teln_get_all_data(Pid,Prx,Data,Acc,LastLine) ->
- case check_for_prompt(Prx,lists:reverse(LastLine) ++ Data) of
+ case check_for_prompt(Prx,LastLine++Data) of
{prompt,Lines,_PromptType,Rest} ->
teln_get_all_data(Pid,Prx,Rest,[Lines|Acc],[]);
{noprompt,Lines,LastLine1} ->
case ct_telnet_client:get_data(Pid) of
{ok,[]} ->
- {ok,lists:reverse(lists:append([Lines|Acc])),
- lists:reverse(LastLine1)};
+ {ok,lists:reverse(lists:append([Lines|Acc])),LastLine1};
{ok,Data1} ->
teln_get_all_data(Pid,Prx,Data1,[Lines|Acc],LastLine1)
end
@@ -879,7 +888,8 @@ teln_get_all_data(Pid,Prx,Data,Acc,LastLine) ->
%% Expect options record
-record(eo,{teln_pid,
prx,
- timeout,
+ idle_timeout,
+ total_timeout,
haltpatterns=[],
seq=false,
repeat=false,
@@ -921,11 +931,12 @@ teln_expect(Name,Pid,Data,Pattern0,Prx,Opts) ->
Seq = get_seq(Opts),
Pattern = convert_pattern(Pattern0,Seq),
- Timeout = get_timeout(Opts),
+ {IdleTimeout,TotalTimeout} = get_timeouts(Opts),
EO = #eo{teln_pid=Pid,
prx=Prx,
- timeout=Timeout,
+ idle_timeout=IdleTimeout,
+ total_timeout=TotalTimeout,
seq=Seq,
haltpatterns=HaltPatterns,
prompt_check=PromptCheck},
@@ -964,11 +975,22 @@ rm_dupl([P|Ps],Acc) ->
rm_dupl([],Acc) ->
lists:reverse(Acc).
-get_timeout(Opts) ->
- case lists:keysearch(timeout,1,Opts) of
- {value,{timeout,T}} -> T;
- false -> ?DEFAULT_TIMEOUT
- end.
+get_timeouts(Opts) ->
+ {case lists:keysearch(idle_timeout,1,Opts) of
+ {value,{_,T}} ->
+ T;
+ false ->
+ %% this check is for backwards compatibility (pre CT v1.8)
+ case lists:keysearch(timeout,1,Opts) of
+ {value,{_,T}} -> T;
+ false -> ?DEFAULT_TIMEOUT
+ end
+ end,
+ case lists:keysearch(total_timeout,1,Opts) of
+ {value,{_,T}} -> T;
+ false -> infinity
+ end}.
+
get_repeat(Opts) ->
case lists:keysearch(repeat,1,Opts) of
{value,{repeat,N}} when is_integer(N) ->
@@ -1010,7 +1032,8 @@ repeat_expect(Name,Pid,Data,Pattern,Acc,EO) ->
{error,Reason}
end.
-teln_expect1(Name,Pid,Data,Pattern,Acc,EO) ->
+teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
+ total_timeout=TotalTO}) ->
ExpectFun = case EO#eo.seq of
true -> fun() ->
seq_expect(Name,Pid,Data,Pattern,Acc,EO)
@@ -1027,12 +1050,12 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO) ->
NotFinished ->
%% Get more data
Fun = fun() -> get_data1(EO#eo.teln_pid) end,
- case ct_gen_conn:do_within_time(Fun, EO#eo.timeout) of
- {error,Reason} ->
+ case timer:tc(ct_gen_conn, do_within_time, [Fun, IdleTO]) of
+ {_,{error,Reason}} ->
%% A timeout will occur when the telnet connection
- %% is idle for EO#eo.timeout milliseconds.
+ %% is idle for EO#eo.idle_timeout milliseconds.
{error,Reason};
- {ok,Data1} ->
+ {_,{ok,Data1}} when TotalTO == infinity ->
case NotFinished of
{nomatch,Rest} ->
%% One expect
@@ -1040,6 +1063,21 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO) ->
{continue,Patterns1,Acc1,Rest} ->
%% Sequence
teln_expect1(Name,Pid,Rest++Data1,Patterns1,Acc1,EO)
+ end;
+ {Elapsed,{ok,Data1}} ->
+ TVal = trunc(TotalTO - (Elapsed/1000)),
+ if TVal =< 0 ->
+ {error,timeout};
+ true ->
+ EO1 = EO#eo{total_timeout = TVal},
+ case NotFinished of
+ {nomatch,Rest} ->
+ %% One expect
+ teln_expect1(Name,Pid,Rest++Data1,Pattern,[],EO1);
+ {continue,Patterns1,Acc1,Rest} ->
+ %% Sequence
+ teln_expect1(Name,Pid,Rest++Data1,Patterns1,Acc1,EO1)
+ end
end
end
end.
@@ -1299,7 +1337,7 @@ teln_receive_until_prompt(Pid,Prx,Timeout) ->
teln_receive_until_prompt(Pid,Prx,Acc,LastLine) ->
{ok,Data} = ct_telnet_client:get_data(Pid),
- case check_for_prompt(Prx,LastLine ++ Data) of
+ case check_for_prompt(Prx,LastLine++Data) of
{prompt,Lines,PromptType,Rest} ->
Return = lists:reverse(lists:append([Lines|Acc])),
{ok,Return,PromptType,Rest};
diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl
index 2cbcba9c77..ce30dcb74b 100644
--- a/lib/common_test/src/ct_telnet_client.erl
+++ b/lib/common_test/src/ct_telnet_client.erl
@@ -32,7 +32,9 @@
-module(ct_telnet_client).
--export([open/1, open/2, open/3, open/4, close/1]).
+%% -define(debug, true).
+
+-export([open/2, open/3, open/4, open/5, close/1]).
-export([send_data/2, get_data/1]).
-define(TELNET_PORT, 23).
@@ -64,20 +66,23 @@
-define(TERMINAL_TYPE, 24).
-define(WINDOW_SIZE, 31).
--record(state,{get_data, keep_alive=true}).
+-record(state,{conn_name, get_data, keep_alive=true, log_pos=1}).
-open(Server) ->
- open(Server, ?TELNET_PORT, ?OPEN_TIMEOUT, true).
+open(Server, ConnName) ->
+ open(Server, ?TELNET_PORT, ?OPEN_TIMEOUT, true, ConnName).
-open(Server, Port) ->
- open(Server, Port, ?OPEN_TIMEOUT, true).
+open(Server, Port, ConnName) ->
+ open(Server, Port, ?OPEN_TIMEOUT, true, ConnName).
-open(Server, Port, Timeout) ->
- open(Server, Port, Timeout, true).
+open(Server, Port, Timeout, ConnName) ->
+ open(Server, Port, Timeout, true, ConnName).
-open(Server, Port, Timeout, KeepAlive) ->
+open(Server, Port, Timeout, KeepAlive, ConnName) ->
Self = self(),
- Pid = spawn(fun() -> init(Self, Server, Port, Timeout, KeepAlive) end),
+ Pid = spawn(fun() ->
+ init(Self, Server, Port, Timeout,
+ KeepAlive, ConnName)
+ end),
receive
{open,Pid} ->
{ok,Pid};
@@ -86,29 +91,34 @@ open(Server, Port, Timeout, KeepAlive) ->
end.
close(Pid) ->
- Pid ! close.
+ Pid ! {close,self()},
+ receive closed -> ok
+ after 5000 -> ok
+ end.
send_data(Pid, Data) ->
Pid ! {send_data, Data++"\n"},
ok.
get_data(Pid) ->
- Pid ! {get_data, self()},
+ Pid ! {get_data,self()},
receive
{data,Data} ->
- {ok, Data}
+ {ok,Data}
end.
%%%-----------------------------------------------------------------
%%% Internal functions
-init(Parent, Server, Port, Timeout, KeepAlive) ->
+init(Parent, Server, Port, Timeout, KeepAlive, ConnName) ->
case gen_tcp:connect(Server, Port, [list,{packet,0}], Timeout) of
{ok,Sock} ->
- dbg("Connected to: ~p (port: ~w, keep_alive: ~w)\n", [Server,Port,KeepAlive]),
- send([?IAC,?DO,?SUPPRESS_GO_AHEAD], Sock),
+ dbg("~p connected to: ~p (port: ~w, keep_alive: ~w)\n",
+ [ConnName,Server,Port,KeepAlive]),
+ send([?IAC,?DO,?SUPPRESS_GO_AHEAD], Sock, ConnName),
Parent ! {open,self()},
- loop(#state{get_data=10, keep_alive=KeepAlive}, Sock, []),
+ loop(#state{conn_name=ConnName, get_data=10, keep_alive=KeepAlive},
+ Sock, []),
gen_tcp:close(Sock);
Error ->
Parent ! {Error,self()}
@@ -118,6 +128,13 @@ loop(State, Sock, Acc) ->
receive
{tcp_closed,_} ->
dbg("Connection closed\n", []),
+ Data = lists:reverse(lists:append(Acc)),
+ dbg("Printing queued messages: ~tp",[Data]),
+ ct_telnet:log(State#state.conn_name,
+ general_io, "~ts",
+ [lists:sublist(Data,
+ State#state.log_pos,
+ length(Data))]),
receive
{get_data,Pid} ->
Pid ! closed
@@ -125,11 +142,11 @@ loop(State, Sock, Acc) ->
ok
end;
{tcp,_,Msg0} ->
- dbg("tcp msg: ~p~n",[Msg0]),
+ dbg("tcp msg: ~tp~n",[Msg0]),
Msg = check_msg(Sock,Msg0,[]),
loop(State, Sock, [Msg | Acc]);
{send_data,Data} ->
- send(Data, Sock),
+ send(Data, Sock, State#state.conn_name),
loop(State, Sock, Acc);
{get_data,Pid} ->
NewState =
@@ -144,54 +161,100 @@ loop(State, Sock, Acc) ->
end;
_ ->
Data = lists:reverse(lists:append(Acc)),
- dbg("get_data ~p\n",[Data]),
+ Len = length(Data),
+ dbg("get_data ~tp\n",[Data]),
+ ct_telnet:log(State#state.conn_name,
+ general_io, "~ts",
+ [lists:sublist(Data,
+ State#state.log_pos,
+ Len)]),
Pid ! {data,Data},
- State
+ State#state{log_pos = 1}
end,
loop(NewState, Sock, []);
{get_data_delayed,Pid} ->
NewState =
case State of
#state{keep_alive = true, get_data = 0} ->
- if Acc == [] -> send([?IAC,?NOP], Sock);
+ if Acc == [] -> send([?IAC,?NOP], Sock,
+ State#state.conn_name);
true -> ok
end,
State#state{get_data=10};
_ ->
State
end,
- NewAcc =
+ {NewAcc,Pos} =
case erlang:is_process_alive(Pid) of
- true ->
+ true when Acc /= [] ->
Data = lists:reverse(lists:append(Acc)),
- dbg("get_data_delayed ~p\n",[Data]),
+ Len = length(Data),
+ dbg("get_data_delayed ~tp\n",[Data]),
+ ct_telnet:log(State#state.conn_name,
+ general_io, "~ts",
+ [lists:sublist(Data,
+ State#state.log_pos,
+ Len)]),
Pid ! {data,Data},
- [];
+ {[],1};
+ true when Acc == [] ->
+ dbg("get_data_delayed nodata\n",[]),
+ Pid ! {data,[]},
+ {[],1};
false ->
- Acc
+ {Acc,NewState#state.log_pos}
end,
- loop(NewState, Sock, NewAcc);
- close ->
+ loop(NewState#state{log_pos=Pos}, Sock, NewAcc);
+ {close,Pid} ->
dbg("Closing connection\n", []),
+ if Acc == [] ->
+ ok;
+ true ->
+ Data = lists:reverse(lists:append(Acc)),
+ dbg("Printing queued messages: ~tp",[Data]),
+ ct_telnet:log(State#state.conn_name,
+ general_io, "~ts",
+ [lists:sublist(Data,
+ State#state.log_pos,
+ length(Data))])
+ end,
gen_tcp:close(Sock),
- ok
+ Pid ! closed
after wait(State#state.keep_alive,?IDLE_TIMEOUT) ->
- if
- Acc == [] -> send([?IAC,?NOP], Sock);
- true -> ok
- end,
- loop(State, Sock, Acc)
+ Data = lists:reverse(lists:append(Acc)),
+ case Data of
+ [] ->
+ send([?IAC,?NOP], Sock, State#state.conn_name),
+ loop(State, Sock, Acc);
+ _ when State#state.log_pos == length(Data)+1 ->
+ loop(State, Sock, Acc);
+ _ ->
+ dbg("Idle timeout, printing ~tp\n",[Data]),
+ Len = length(Data),
+ ct_telnet:log(State#state.conn_name,
+ general_io, "~ts",
+ [lists:sublist(Data,
+ State#state.log_pos,
+ Len)]),
+ loop(State#state{log_pos = Len+1}, Sock, Acc)
+ end
end.
wait(true, Time) -> Time;
wait(false, _) -> infinity.
-send(Data, Sock) ->
+send(Data, Sock, ConnName) ->
case Data of
[?IAC|_] = Cmd ->
cmd_dbg(Cmd);
_ ->
- dbg("Sending: ~p\n", [Data])
+ dbg("Sending: ~tp\n", [Data]),
+ try io_lib:format("[~w] ~ts", [?MODULE,Data]) of
+ Str ->
+ ct_telnet:log(ConnName, general_io, Str, [])
+ catch
+ _:_ -> ok
+ end
end,
gen_tcp:send(Sock, Data),
ok.
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index c07ea323e6..10a9bdac67 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -1120,8 +1120,9 @@ should_be_added(Tag,Node,_Data,Spec) ->
%% list terms *without* possible duplicates here
Tag == logdir; Tag == logopts;
Tag == basic_html; Tag == label;
- Tag == auto_compile; Tag == stylesheet;
- Tag == verbosity; Tag == silent_connections ->
+ Tag == auto_compile; Tag == abort_if_missing_suites;
+ Tag == stylesheet; Tag == verbosity;
+ Tag == silent_connections ->
lists:keymember(ref2node(Node,Spec#testspec.nodes),1,
read_field(Spec,Tag)) == false;
%% for terms *with* possible duplicates
@@ -1496,6 +1497,8 @@ valid_terms() ->
{include,3},
{auto_compile,2},
{auto_compile,3},
+ {abort_if_missing_suites,2},
+ {abort_if_missing_suites,3},
{stylesheet,2},
{stylesheet,3},
{suites,3},
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index f5eb3a72f0..56027586d1 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -37,7 +37,7 @@
save_suite_data_async/3, save_suite_data_async/2,
read_suite_data/1,
delete_suite_data/0, delete_suite_data/1, match_delete_suite_data/1,
- delete_testdata/0, delete_testdata/1,
+ delete_testdata/0, delete_testdata/1, match_delete_testdata/1,
set_testdata/1, get_testdata/1, get_testdata/2,
set_testdata_async/1, update_testdata/2, update_testdata/3,
set_verbosity/1, get_verbosity/1]).
@@ -270,6 +270,9 @@ delete_testdata() ->
delete_testdata(Key) ->
call({delete_testdata, Key}).
+match_delete_testdata(KeyPat) ->
+ call({match_delete_testdata, KeyPat}).
+
update_testdata(Key, Fun) ->
update_testdata(Key, Fun, []).
@@ -361,7 +364,25 @@ loop(Mode,TestData,StartDir) ->
{{delete_testdata,Key},From} ->
TestData1 = lists:keydelete(Key,1,TestData),
return(From,ok),
- loop(From,TestData1,StartDir);
+ loop(From,TestData1,StartDir);
+ {{match_delete_testdata,{Key1,Key2}},From} ->
+ %% handles keys with 2 elements
+ TestData1 =
+ lists:filter(fun({Key,_}) when not is_tuple(Key) ->
+ true;
+ ({Key,_}) when tuple_size(Key) =/= 2 ->
+ true;
+ ({{_,KeyB},_}) when Key1 == '_' ->
+ KeyB =/= Key2;
+ ({{KeyA,_},_}) when Key2 == '_' ->
+ KeyA =/= Key1;
+ (_) when Key1 == '_' ; Key2 == '_' ->
+ false;
+ (_) ->
+ true
+ end, TestData),
+ return(From,ok),
+ loop(From,TestData1,StartDir);
{{set_testdata,New = {Key,_Val}},From} ->
TestData1 = lists:keydelete(Key,1,TestData),
return(From,ok),
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index a82d58cc42..845bb55486 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -48,6 +48,7 @@
release_shell=false,
include=[],
auto_compile=[],
+ abort_if_missing_suites=[],
stylesheet=[],
multiply_timetraps=[],
scale_timetraps=[],
diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl
index a731c8054c..1e60f2751e 100644
--- a/lib/common_test/src/cth_conn_log.erl
+++ b/lib/common_test/src/cth_conn_log.erl
@@ -91,16 +91,18 @@ merge_log_info([{Mod,ConfOpts}|ConfList],HookList) ->
{value,{_,HookOpts},HL1} ->
{ConfOpts ++ HookOpts, HL1} % ConfOpts overwrites HookOpts!
end,
- [{Mod,get_log_opts(Opts)} | merge_log_info(ConfList,HookList1)];
+ [{Mod,get_log_opts(Mod,Opts)} | merge_log_info(ConfList,HookList1)];
merge_log_info([],HookList) ->
- [{Mod,get_log_opts(Opts)} || {Mod,Opts} <- HookList].
+ [{Mod,get_log_opts(Mod,Opts)} || {Mod,Opts} <- HookList].
-get_log_opts(Opts) ->
- LogType = proplists:get_value(log_type,Opts,html),
+get_log_opts(Mod,Opts) ->
+ DefaultLogType = if Mod == ct_telnet -> raw;
+ true -> html
+ end,
+ LogType = proplists:get_value(log_type,Opts,DefaultLogType),
Hosts = proplists:get_value(hosts,Opts,[]),
{LogType,Hosts}.
-
pre_init_per_testcase(TestCase,Config,CthState) ->
Logs =
lists:map(
diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl
index 7ed2018bdf..bb12171ea7 100644
--- a/lib/common_test/src/cth_surefire.erl
+++ b/lib/common_test/src/cth_surefire.erl
@@ -79,6 +79,10 @@ init(Path, Opts) ->
url_base = proplists:get_value(url_base,Opts),
timer = now() }.
+pre_init_per_suite(Suite,SkipOrFail,State) when is_tuple(SkipOrFail) ->
+ {SkipOrFail, init_tc(State#state{curr_suite = Suite,
+ curr_suite_ts = now()},
+ SkipOrFail) };
pre_init_per_suite(Suite,Config,#state{ test_cases = [] } = State) ->
TcLog = proplists:get_value(tc_logfile,Config),
CurrLogDir = filename:dirname(TcLog),
diff --git a/lib/common_test/src/unix_telnet.erl b/lib/common_test/src/unix_telnet.erl
index e049c3bf39..10666b979d 100644
--- a/lib/common_test/src/unix_telnet.erl
+++ b/lib/common_test/src/unix_telnet.erl
@@ -17,8 +17,8 @@
%% %CopyrightEnd%
%%
-%%% @doc Callback module for ct_telnet for talking telnet
-%%% to a unix host.
+%%% @doc Callback module for ct_telnet, for connecting to a telnet
+%%% server on a unix host.
%%%
%%% <p>It requires the following entry in the config file:</p>
%%% <pre>
@@ -28,15 +28,15 @@
%%% {password,Password},
%%% {keep_alive,Bool}]}. % optional</pre>
%%%
-%%% <p>To talk telnet to the host specified by
+%%% <p>To communicate via telnet to the host specified by
%%% <code>HostNameOrIpAddress</code>, use the interface functions in
-%%% <code>ct</code>, e.g. <code>open(Name), cmd(Name,Cmd), ...</code>.</p>
+%%% <code>ct_telnet</code>, e.g. <code>open(Name), cmd(Name,Cmd), ...</code>.</p>
%%%
%%% <p><code>Name</code> is the name you allocated to the unix host in
%%% your <code>require</code> statement. E.g.</p>
-%%% <pre> suite() -> [{require,Name,{unix,[telnet,username,password]}}].</pre>
+%%% <pre> suite() -> [{require,Name,{unix,[telnet]}}].</pre>
%%% <p>or</p>
-%%% <pre> ct:require(Name,{unix,[telnet,username,password]}).</pre>
+%%% <pre> ct:require(Name,{unix,[telnet]}).</pre>
%%%
%%% <p>The "keep alive" activity (i.e. that Common Test sends NOP to the server
%%% every 10 seconds if the connection is idle) may be enabled or disabled for one
@@ -62,20 +62,18 @@
-define(prx,"login: |Password: |\\\$ |> ").
%%%-----------------------------------------------------------------
-%%% @hidden
%%% @spec get_prompt_regexp() -> PromptRegexp
%%% PromptRegexp = ct_telnet:prompt_regexp()
%%%
%%% @doc Callback for ct_telnet.erl.
%%%
-%%% <p>Return the prompt regexp for telnet connections to the
-%%% interwatch instrument.</p>
+%%% <p>Return a suitable regexp string that will match common
+%%% prompts for users on unix hosts.</p>
get_prompt_regexp() ->
?prx.
%%%-----------------------------------------------------------------
-%%% @hidden
%%% @spec connect(ConnName,Ip,Port,Timeout,KeepAlive,Extra) ->
%%% {ok,Handle} | {error,Reason}
%%% ConnName = ct:target_name()
@@ -83,14 +81,15 @@ get_prompt_regexp() ->
%%% Port = integer()
%%% Timeout = integer()
%%% KeepAlive = bool()
-%%% Extra = {Username,Password}
+%%% Extra = ct:target_name() | {Username,Password}
%%% Username = string()
%%% Password = string()
%%% Handle = ct_telnet:handle()
+%%% Reason = term()
%%%
%%% @doc Callback for ct_telnet.erl.
%%%
-%%% <p>Setup telnet connection to a UNIX host.</p>
+%%% <p>Setup telnet connection to a unix host.</p>
connect(ConnName,Ip,Port,Timeout,KeepAlive,Extra) ->
case Extra of
{Username,Password} ->
@@ -109,7 +108,7 @@ connect(ConnName,Ip,Port,Timeout,KeepAlive,Extra) ->
connect1(Name,Ip,Port,Timeout,KeepAlive,Username,Password) ->
start_gen_log("unix_telnet connect"),
Result =
- case ct_telnet_client:open(Ip,Port,Timeout,KeepAlive) of
+ case ct_telnet_client:open(Ip,Port,Timeout,KeepAlive,Name) of
{ok,Pid} ->
case ct_telnet:silent_teln_expect(Name,Pid,[],
[prompt],?prx,[]) of
@@ -143,13 +142,13 @@ connect1(Name,Ip,Port,Timeout,KeepAlive,Username,Password) ->
{ok,[{prompt,_OtherPrompt1},{prompt,_OtherPrompt2}],_} ->
{ok,Pid};
Error ->
- log(Name,error,
+ log(Name,conn_error,
"Did not get expected prompt from ~p:~p\n~p\n",
[Ip,Port,Error]),
{error,Error}
end;
Error ->
- log(Name,error,
+ log(Name,conn_error,
"Could not open telnet connection to ~p:~p\n~p\n",
[Ip,Port,Error]),
Error
diff --git a/lib/common_test/test/ct_config_info_SUITE.erl b/lib/common_test/test/ct_config_info_SUITE.erl
index 8f2f0eb75f..9c242a41df 100644
--- a/lib/common_test/test/ct_config_info_SUITE.erl
+++ b/lib/common_test/test/ct_config_info_SUITE.erl
@@ -125,7 +125,7 @@ test_events(config_info) ->
[{?eh,tc_start,{config_info_1_SUITE,{init_per_group,g1,[]}}},
{?eh,tc_done,{config_info_1_SUITE,{init_per_group,g1,[]},
{failed,{timetrap_timeout,350}}}},
- {?eh,tc_auto_skip,{config_info_1_SUITE,t11,
+ {?eh,tc_auto_skip,{config_info_1_SUITE,{t11,g1},
{failed,{config_info_1_SUITE,init_per_group,{timetrap_timeout,350}}}}},
{?eh,tc_auto_skip,{config_info_1_SUITE,{end_per_group,g1},
{failed,{config_info_1_SUITE,init_per_group,
@@ -142,7 +142,7 @@ test_events(config_info) ->
[{?eh,tc_start,{config_info_1_SUITE,{init_per_group,g4,[]}}},
{?eh,tc_done,{config_info_1_SUITE,{init_per_group,g4,[]},
{failed,{timetrap_timeout,400}}}},
- {?eh,tc_auto_skip,{config_info_1_SUITE,t41,
+ {?eh,tc_auto_skip,{config_info_1_SUITE,{t41,g4},
{failed,{config_info_1_SUITE,init_per_group,
{timetrap_timeout,400}}}}},
{?eh,tc_auto_skip,{config_info_1_SUITE,{end_per_group,g4},
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index 194e7d42ae..ecf231529a 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -369,8 +369,8 @@ test_events(cfg_error) ->
{'EXIT',init_per_suite_fails}}}}},
{?eh,test_stats,{0,0,{0,1}}},
{?eh,tc_auto_skip,
- {cfg_error_1_SUITE,tc2,{failed,{cfg_error_1_SUITE,init_per_suite,
- {'EXIT',init_per_suite_fails}}}}},
+ {cfg_error_1_SUITE,{tc2,g1},{failed,{cfg_error_1_SUITE,init_per_suite,
+ {'EXIT',init_per_suite_fails}}}}},
{?eh,test_stats,{0,0,{0,2}}},
{?eh,tc_auto_skip,
{cfg_error_1_SUITE,end_per_suite,{failed,{cfg_error_1_SUITE,init_per_suite,
@@ -386,7 +386,7 @@ test_events(cfg_error) ->
{'EXIT',{{badmatch,[1,2]},'_'}}}}}},
{?eh,test_stats,{0,0,{0,3}}},
{?eh,tc_auto_skip,
- {cfg_error_2_SUITE,tc2,
+ {cfg_error_2_SUITE,{tc2,g1},
{failed,{cfg_error_2_SUITE,init_per_suite,
{'EXIT',{{badmatch,[1,2]},'_'}}}}}},
{?eh,test_stats,{0,0,{0,4}}},
@@ -403,7 +403,7 @@ test_events(cfg_error) ->
{failed,{cfg_error_3_SUITE,init_per_suite,{timetrap_timeout,2000}}}}},
{?eh,test_stats,{0,0,{0,5}}},
{?eh,tc_auto_skip,
- {cfg_error_3_SUITE,tc2,
+ {cfg_error_3_SUITE,{tc2,g1},
{failed,{cfg_error_3_SUITE,init_per_suite,{timetrap_timeout,2000}}}}},
{?eh,test_stats,{0,0,{0,6}}},
{?eh,tc_auto_skip,
@@ -417,7 +417,7 @@ test_events(cfg_error) ->
{failed,{cfg_error_4_SUITE,init_per_suite,bad_return}}}},
{?eh,test_stats,{0,0,{0,7}}},
{?eh,tc_auto_skip,
- {cfg_error_4_SUITE,tc2,
+ {cfg_error_4_SUITE,{tc2,g1},
{failed,{cfg_error_4_SUITE,init_per_suite,bad_return}}}},
{?eh,test_stats,{0,0,{0,8}}},
{?eh,tc_auto_skip,
@@ -431,7 +431,7 @@ test_events(cfg_error) ->
{failed,{cfg_error_5_SUITE,init_per_suite,bad_return}}}},
{?eh,test_stats,{0,0,{0,9}}},
{?eh,tc_auto_skip,
- {cfg_error_5_SUITE,tc2,
+ {cfg_error_5_SUITE,{tc2,g1},
{failed,{cfg_error_5_SUITE,init_per_suite,bad_return}}}},
{?eh,test_stats,{0,0,{0,10}}},
{?eh,tc_auto_skip,
@@ -477,7 +477,7 @@ test_events(cfg_error) ->
{cfg_error_8_SUITE,{init_per_group,g1,[]},
{failed,{error,{init_per_group_fails,g1}}}}},
{?eh,tc_auto_skip,
- {cfg_error_8_SUITE,tc1,
+ {cfg_error_8_SUITE,{tc1,g1},
{failed,{cfg_error_8_SUITE,init_per_group,
{'EXIT',{init_per_group_fails,g1}}}}}},
{?eh,test_stats,{4,0,{0,11}}},
@@ -489,7 +489,7 @@ test_events(cfg_error) ->
[{?eh,tc_start,{cfg_error_8_SUITE,{init_per_group,g2,[]}}},
{?eh,tc_done,{cfg_error_8_SUITE,{init_per_group,g2,[]},
{failed,{timetrap_timeout,2000}}}},
- {?eh,tc_auto_skip,{cfg_error_8_SUITE,tc1,
+ {?eh,tc_auto_skip,{cfg_error_8_SUITE,{tc1,g2},
{failed,{cfg_error_8_SUITE,init_per_group,
{timetrap_timeout,2000}}}}},
{?eh,test_stats,{4,0,{0,12}}},
@@ -502,7 +502,7 @@ test_events(cfg_error) ->
{cfg_error_8_SUITE,{init_per_group,g3,[]},
{failed,{error,{{badmatch,42},'_'}}}}},
{?eh,tc_auto_skip,
- {cfg_error_8_SUITE,tc1,
+ {cfg_error_8_SUITE,{tc1,g3},
{failed,{cfg_error_8_SUITE,init_per_group,
{'EXIT',{{badmatch,42},'_'}}}}}},
{?eh,test_stats,{4,0,{0,13}}},
@@ -528,7 +528,7 @@ test_events(cfg_error) ->
{?eh,tc_done,{cfg_error_8_SUITE,{init_per_group,g6,[]},
{failed,{error,{sub_group_failed,g6}}}}},
{?eh,tc_auto_skip,
- {cfg_error_8_SUITE,tc2,
+ {cfg_error_8_SUITE,{tc2,g6},
{failed,{cfg_error_8_SUITE,init_per_group,
{'EXIT',{sub_group_failed,g6}}}}}},
{?eh,test_stats,{6,0,{0,14}}},
@@ -1111,11 +1111,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g4,[]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g4,[]},
{user_timetrap_error,{kaboom,'_'}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,g4},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{0,11,{0,1}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,g4},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{0,11,{0,2}}},
@@ -1126,11 +1126,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g5,[]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g5,[]},
{user_timetrap_error,{kaboom,'_'}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,g5},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{0,11,{0,3}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,g5},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{0,11,{0,4}}},
@@ -1141,11 +1141,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g6,[]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g6,[]},
{failed,{timetrap_timeout,{'$approx',500}}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,g6},
{failed,{timetrap_8_SUITE,init_per_group,
{timetrap_timeout,'_'}}}}},
{?eh,test_stats,{0,11,{0,5}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,g6},
{failed,{timetrap_8_SUITE,init_per_group,
{timetrap_timeout,'_'}}}}},
{?eh,test_stats,{0,11,{0,6}}},
@@ -1294,11 +1294,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg4,[parallel]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg4,[parallel]},
{user_timetrap_error,{kaboom,'_'}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,pg4},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{4,26,{0,7}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,pg4},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{4,26,{0,8}}},
@@ -1310,11 +1310,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg5,[parallel]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg5,[parallel]},
{user_timetrap_error,{kaboom,'_'}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,pg5},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{4,26,{0,9}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,pg5},
{failed,{timetrap_8_SUITE,init_per_group,
{user_timetrap_error,{kaboom,'_'}}}}}},
{?eh,test_stats,{4,26,{0,10}}},
@@ -1326,11 +1326,11 @@ test_events(timetrap_fun_group) ->
[{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg6,[parallel]}}},
{?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg6,[parallel]},
{failed,{timetrap_timeout,{'$approx',500}}}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc0,pg6},
{failed,{timetrap_8_SUITE,init_per_group,
{timetrap_timeout,'_'}}}}},
{?eh,test_stats,{4,26,{0,11}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,pg6},
{failed,{timetrap_8_SUITE,init_per_group,
{timetrap_timeout,'_'}}}}},
{?eh,test_stats,{4,26,{0,12}}},
@@ -1407,10 +1407,10 @@ test_events(timetrap_fun_group) ->
{?eh,tc_done,{timetrap_8_SUITE,tc0,
{user_timetrap_error,{kaboom,'_'}}}},
{?eh,test_stats,{9,31,{0,12}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc1,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc1,sg1},
{failed,{timetrap_8_SUITE,tc0}}}},
{?eh,test_stats,{9,31,{0,13}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,sg1},
{failed,{timetrap_8_SUITE,tc0}}}},
{?eh,test_stats,{9,31,{0,14}}},
{?eh,tc_start,{timetrap_8_SUITE,{end_per_group,sg1,[sequence]}}},
@@ -1425,10 +1425,10 @@ test_events(timetrap_fun_group) ->
{?eh,tc_done,{timetrap_8_SUITE,tc0,
{failed,{timetrap_timeout,{'$approx',1000}}}}},
{?eh,test_stats,{10,32,{0,14}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc1,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc1,sg2},
{failed,{timetrap_8_SUITE,tc0}}}},
{?eh,test_stats,{10,32,{0,15}}},
- {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,{tc2,sg2},
{failed,{timetrap_8_SUITE,tc0}}}},
{?eh,test_stats,{10,32,{0,16}}},
{?eh,tc_start,{timetrap_8_SUITE,{end_per_group,sg2,[sequence]}}},
diff --git a/lib/common_test/test/ct_group_info_SUITE.erl b/lib/common_test/test/ct_group_info_SUITE.erl
index e7bc5baaa1..83ac7dbbcf 100644
--- a/lib/common_test/test/ct_group_info_SUITE.erl
+++ b/lib/common_test/test/ct_group_info_SUITE.erl
@@ -273,7 +273,7 @@ test_events(timetrap_all) ->
{init_per_group,g11,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
{?eh,tc_auto_skip,
- {group_timetrap_1_SUITE,t111,{group0_failed,bad_return_value}}},
+ {group_timetrap_1_SUITE,{t111,g11},{group0_failed,bad_return_value}}},
{?eh,test_stats,{0,13,{0,1}}},
{?eh,tc_auto_skip,{group_timetrap_1_SUITE,
{end_per_group,g11},
@@ -431,7 +431,7 @@ test_events(timetrap_all_no_ips) ->
{?eh,tc_done,{group_timetrap_2_SUITE,
{init_per_group,g11,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_timetrap_2_SUITE,t111,
+ {?eh,tc_auto_skip,{group_timetrap_2_SUITE,{t111,g11},
{group0_failed,bad_return_value}}},
{?eh,test_stats,{0,13,{0,1}}},
{?eh,tc_auto_skip,{group_timetrap_2_SUITE,
@@ -512,7 +512,7 @@ test_events(timetrap_all_no_ipg) ->
{?eh,tc_done,{ct_framework,
{init_per_group,g11,[{suite,group_timetrap_3_SUITE}]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_timetrap_3_SUITE,t111,{group0_failed,bad_return_value}}},
+ {?eh,tc_auto_skip,{group_timetrap_3_SUITE,{t111,g11},{group0_failed,bad_return_value}}},
{?eh,test_stats,{0,13,{0,1}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g11},
{group0_failed,bad_return_value}}}],
@@ -551,7 +551,7 @@ test_events(require) ->
{?eh,tc_done,{group_require_1_SUITE,{init_per_group,g4,[]},
{auto_skipped,{require_failed,
{name_in_use,common2_alias,common2}}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t41,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t41,g4},
{require_failed,
{name_in_use,common2_alias,common2}}}},
{?eh,test_stats,{4,0,{0,1}}},
@@ -580,7 +580,7 @@ test_events(require) ->
{init_per_group,g8,[]},
{auto_skipped,{require_failed,
{not_available,non_existing}}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t81,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t81,g8},
{require_failed,{not_available,non_existing}}}},
{?eh,test_stats,{8,0,{0,2}}},
{?eh,tc_auto_skip,{group_require_1_SUITE,{end_per_group,g8},
@@ -604,7 +604,7 @@ test_events(require) ->
{?eh,tc_done,{group_require_1_SUITE,
{init_per_group,g11,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t111,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t111,g11},
{group0_failed,bad_return_value}}},
{?eh,test_stats,{9,0,{0,4}}},
{?eh,tc_auto_skip,{group_require_1_SUITE,
@@ -646,7 +646,7 @@ test_events(require_default) ->
{?eh,tc_done,{group_require_1_SUITE,
{init_per_group,g4,[]},
{auto_skipped,{require_failed,{not_available,common3}}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t41,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t41,g4},
{require_failed,{not_available,common3}}}},
{?eh,test_stats,{4,0,{0,1}}},
{?eh,tc_auto_skip,{group_require_1_SUITE,{end_per_group,g4},
@@ -674,7 +674,7 @@ test_events(require_default) ->
{init_per_group,g8,[]},
{auto_skipped,{require_failed,
{not_available,non_existing}}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t81,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t81,g8},
{require_failed,{not_available,non_existing}}}},
{?eh,test_stats,{8,0,{0,2}}},
{?eh,tc_auto_skip,{group_require_1_SUITE,{end_per_group,g8},
@@ -699,7 +699,7 @@ test_events(require_default) ->
{?eh,tc_done,{group_require_1_SUITE,
{init_per_group,g11,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_require_1_SUITE,t111,
+ {?eh,tc_auto_skip,{group_require_1_SUITE,{t111,g11},
{group0_failed,bad_return_value}}},
{?eh,test_stats,{9,0,{0,4}}},
{?eh,tc_auto_skip,{group_require_1_SUITE,
@@ -740,7 +740,7 @@ test_events(require_no_ips) ->
{?eh,tc_done,{group_require_2_SUITE,{init_per_group,g4,[]},
{auto_skipped,{require_failed,
{name_in_use,common2_alias,common2}}}}},
- {?eh,tc_auto_skip,{group_require_2_SUITE,t41,
+ {?eh,tc_auto_skip,{group_require_2_SUITE,{t41,g4},
{require_failed,{name_in_use,common2_alias,common2}}}},
{?eh,test_stats,{4,0,{0,1}}},
{?eh,tc_auto_skip,{group_require_2_SUITE,{end_per_group,g4},
@@ -768,7 +768,7 @@ test_events(require_no_ips) ->
{init_per_group,g8,[]},
{auto_skipped,{require_failed,
{not_available,non_existing}}}}},
- {?eh,tc_auto_skip,{group_require_2_SUITE,t81,
+ {?eh,tc_auto_skip,{group_require_2_SUITE,{t81,g8},
{require_failed,{not_available,non_existing}}}},
{?eh,test_stats,{8,0,{0,2}}},
{?eh,tc_auto_skip,{group_require_2_SUITE,{end_per_group,g8},
@@ -792,7 +792,7 @@ test_events(require_no_ips) ->
{?eh,tc_done,{group_require_2_SUITE,
{init_per_group,g11,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_require_2_SUITE,t111,
+ {?eh,tc_auto_skip,{group_require_2_SUITE,{t111,g11},
{group0_failed,bad_return_value}}},
{?eh,test_stats,{9,0,{0,4}}},
{?eh,tc_auto_skip,{group_require_2_SUITE,
@@ -831,7 +831,7 @@ test_events(require_no_ipg) ->
[{?eh,tc_start,{ct_framework,{init_per_group,g4,[{suite,group_require_3_SUITE}]}}},
{?eh,tc_done,{ct_framework,{init_per_group,g4,[{suite,group_require_3_SUITE}]},
{auto_skipped,{require_failed,{name_in_use,common2_alias,common2}}}}},
- {?eh,tc_auto_skip,{group_require_3_SUITE,t41,
+ {?eh,tc_auto_skip,{group_require_3_SUITE,{t41,g4},
{require_failed,{name_in_use,common2_alias,common2}}}},
{?eh,test_stats,{4,0,{0,1}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g4},
@@ -857,7 +857,7 @@ test_events(require_no_ipg) ->
[{?eh,tc_start,{ct_framework,{init_per_group,g8,[{suite,group_require_3_SUITE}]}}},
{?eh,tc_done,{ct_framework,{init_per_group,g8,[{suite,group_require_3_SUITE}]},
{auto_skipped,{require_failed,{not_available,non_existing}}}}},
- {?eh,tc_auto_skip,{group_require_3_SUITE,t81,
+ {?eh,tc_auto_skip,{group_require_3_SUITE,{t81,g8},
{require_failed,{not_available,non_existing}}}},
{?eh,test_stats,{8,0,{0,2}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g8},
@@ -879,7 +879,7 @@ test_events(require_no_ipg) ->
[{?eh,tc_start,{ct_framework,{init_per_group,g11,[{suite,group_require_3_SUITE}]}}},
{?eh,tc_done,{ct_framework,{init_per_group,g11,[{suite,group_require_3_SUITE}]},
{auto_skipped,{group0_failed,bad_return_value}}}},
- {?eh,tc_auto_skip,{group_require_3_SUITE,t111,{group0_failed,bad_return_value}}},
+ {?eh,tc_auto_skip,{group_require_3_SUITE,{t111,g11},{group0_failed,bad_return_value}}},
{?eh,test_stats,{9,0,{0,4}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g11},
{group0_failed,bad_return_value}}}],
diff --git a/lib/common_test/test/ct_groups_spec_SUITE.erl b/lib/common_test/test/ct_groups_spec_SUITE.erl
index 5a6d5ac0ac..de4ab77229 100644
--- a/lib/common_test/test/ct_groups_spec_SUITE.erl
+++ b/lib/common_test/test/ct_groups_spec_SUITE.erl
@@ -246,7 +246,8 @@ test_events(override_with_all) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g1,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t11,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t12,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t13,{failed,{groups_spec_1_SUITE,t12}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t13,g1},
+ {failed,{groups_spec_1_SUITE,t12}}}},
{?eh,test_stats,{3,2,{0,1}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]},ok}}],
@@ -327,19 +328,27 @@ test_events(override_with_all) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g3,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t31,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t32,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t33,{failed,{groups_spec_1_SUITE,t32}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t33,g3},
+ {failed,{groups_spec_1_SUITE,t32}}}},
{?eh,test_stats,{14,9,{0,2}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]},ok}}],
{?eh,tc_done,{groups_spec_1_SUITE,t22,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t41,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t51,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t52,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t53,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t54,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t42,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t23,{failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t41,g4},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t51,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t52,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t53,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t54,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t42,g4},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t23,g2},
+ {failed,{groups_spec_1_SUITE,t22}}}},
{?eh,test_stats,{14,10,{0,9}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g2,[sequence]}}},
@@ -355,7 +364,8 @@ test_events(override_with_all) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g3,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t31,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t32,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t33,{failed,{groups_spec_1_SUITE,t32}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t33,g3},
+ {failed,{groups_spec_1_SUITE,t32}}}},
{?eh,test_stats,{16,11,{0,10}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]},ok}}],
@@ -372,8 +382,10 @@ test_events(override_with_all) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g5,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t51,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t52,{failed,{timetrap_timeout,2000}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t53,{failed,{groups_spec_1_SUITE,t52}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t54,{failed,{groups_spec_1_SUITE,t52}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t53,g5},
+ {failed,{groups_spec_1_SUITE,t52}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t54,g5},
+ {failed,{groups_spec_1_SUITE,t52}}}},
{?eh,test_stats,{18,13,{0,12}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g5,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g5,[sequence]},ok}}],
@@ -417,7 +429,8 @@ test_events(override_with_spec) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g1,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t11,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t12,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t13,{failed,{groups_spec_1_SUITE,t12}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t13,g1},
+ {failed,{groups_spec_1_SUITE,t12}}}},
{?eh,test_stats,{3,2,{0,1}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]},ok}}],
@@ -493,18 +506,26 @@ test_events(override_with_spec) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g3,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t31,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t32,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t33,{failed,{groups_spec_1_SUITE,t32}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t33,g3},
+ {failed,{groups_spec_1_SUITE,t32}}}},
{?eh,test_stats,{14,9,{0,2}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]},ok}}],
{?eh,tc_done,{groups_spec_1_SUITE,t22,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t41,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t51,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t52,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t53,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t54,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t42,{failed,{groups_spec_1_SUITE,t22}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t23,{failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t41,g4},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t51,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t52,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t53,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t54,g5},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t42,g4},
+ {failed,{groups_spec_1_SUITE,t22}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t23,g2},
+ {failed,{groups_spec_1_SUITE,t22}}}},
{?eh,test_stats,{14,10,{0,9}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g2,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g2,[sequence]},ok}}],
@@ -521,7 +542,8 @@ test_events(override_with_spec) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g3,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t31,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t32,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t33,{failed,{groups_spec_1_SUITE,t32}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t33,g3},
+ {failed,{groups_spec_1_SUITE,t32}}}},
{?eh,test_stats,{16,11,{0,10}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g3,[sequence]},ok}}],
@@ -535,8 +557,10 @@ test_events(override_with_spec) ->
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g5,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t51,ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t52,{failed,{timetrap_timeout,2000}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t53,{failed,{groups_spec_1_SUITE,t52}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t54,{failed,{groups_spec_1_SUITE,t52}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t53,g5},
+ {failed,{groups_spec_1_SUITE,t52}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t54,g5},
+ {failed,{groups_spec_1_SUITE,t52}}}},
{?eh,test_stats,{18,13,{0,12}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g5,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g5,[sequence]},ok}}],
@@ -555,7 +579,8 @@ test_events(override_with_spec) ->
[{?eh,tc_start,{groups_spec_1_SUITE,{init_per_group,g1,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{init_per_group,g1,[sequence]},ok}},
{?eh,tc_done,{groups_spec_1_SUITE,t12,{failed,{error,crashes}}}},
- {?eh,tc_auto_skip,{groups_spec_1_SUITE,t13,{failed,{groups_spec_1_SUITE,t12}}}},
+ {?eh,tc_auto_skip,{groups_spec_1_SUITE,{t13,g1},
+ {failed,{groups_spec_1_SUITE,t12}}}},
{?eh,test_stats,{19,15,{0,13}}},
{?eh,tc_start,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]}}},
{?eh,tc_done,{groups_spec_1_SUITE,{end_per_group,g1,[sequence]},ok}}],
diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl
index b5855da9df..c8fc4bd59b 100644
--- a/lib/common_test/test/ct_hooks_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE.erl
@@ -786,7 +786,7 @@ test_events(skip_pre_end_cth) ->
{?eh,cth,{'_',post_end_per_group,[group1,'$proplist','_',[]]}},
{?eh,tc_done,{ct_scope_per_group_cth_SUITE,{end_per_group,group1,[]},
{skipped,"Test skip"}}}],
- {?eh,cth,{'_',on_tc_skip,[end_per_group,
+ {?eh,cth,{'_',on_tc_skip,[{end_per_group,group1},
{tc_user_skip,{skipped,"Test skip"}},
[]]}},
{?eh,tc_start,{ct_scope_per_group_cth_SUITE,end_per_suite}},
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
index 9ee2a90896..6caac7e447 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
@@ -229,9 +229,9 @@ post_end_per_testcase(TC,Config,Return,State) ->
%% This function should be used for extra cleanup which might be needed.
%% It is not possible to modify the config or the status of the test run.
-spec on_tc_fail(TC :: init_per_suite | end_per_suite |
- init_per_group | end_per_group | atom(),
- Reason :: term(), State :: #state{}) ->
- NewState :: #state{}.
+ init_per_group | end_per_group | atom() |
+ {Function :: atom(), GroupName :: atom()},
+ Reason :: term(), State :: #state{}) -> NewState :: #state{}.
on_tc_fail(TC, Reason, State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
@@ -243,11 +243,11 @@ on_tc_fail(TC, Reason, State) ->
%% or due to an init function failing. Test case can be
%% end_per_suite, init_per_group, end_per_group and the actual test cases.
-spec on_tc_skip(TC :: end_per_suite |
- init_per_group | end_per_group | atom(),
+ init_per_group | end_per_group | atom() |
+ {Function :: atom(), GroupName :: atom()},
{tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} |
- {tc_user_skip, {skipped, Reason :: term()}},
- State :: #state{}) ->
- NewState :: #state{}.
+ {tc_user_skip, {skipped, Reason :: term()}},
+ State :: #state{}) -> NewState :: #state{}.
on_tc_skip(TC, Reason, State) ->
gen_event:notify(
?CT_EVMGR_REF, #event{ name = cth, node = node(),
diff --git a/lib/common_test/test/ct_master_SUITE.erl b/lib/common_test/test/ct_master_SUITE.erl
index 7408cbe376..e90513f888 100644
--- a/lib/common_test/test/ct_master_SUITE.erl
+++ b/lib/common_test/test/ct_master_SUITE.erl
@@ -81,7 +81,8 @@ end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{timetrap,{seconds,60}},
+ {ct_hooks,[ts_install_cth]}].
all() ->
[ct_master_test].
diff --git a/lib/common_test/test/ct_repeat_1_SUITE.erl b/lib/common_test/test/ct_repeat_1_SUITE.erl
index 98eaa28763..e37aeb196c 100644
--- a/lib/common_test/test/ct_repeat_1_SUITE.erl
+++ b/lib/common_test/test/ct_repeat_1_SUITE.erl
@@ -225,7 +225,7 @@ test_events(repeat_cs_and_grs) ->
{?eh,test_stats,{3,1,{0,0}}},
[{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_init,[]},
{failed,{error,fails_on_purpose}}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_fail_init},
{failed,{repeat_1_SUITE,init_per_group,
{'EXIT',fails_on_purpose}}}}},
{?eh,test_stats,{3,1,{0,1}}},
@@ -247,7 +247,7 @@ test_events(repeat_cs_and_grs) ->
{?eh,test_stats,{7,2,{0,1}}},
[{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_init,[]},
{failed,{error,fails_on_purpose}}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_fail_init},
{failed,{repeat_1_SUITE,init_per_group,
{'EXIT',fails_on_purpose}}}}},
{?eh,test_stats,{7,2,{0,2}}},
@@ -269,7 +269,7 @@ test_events(repeat_seq) ->
ok}},
{?eh,test_stats,{1,0,{0,0}}},
{?eh,test_stats,{1,1,{0,0}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_2,repeat_seq_1},
{failed,{repeat_1_SUITE,tc_fail_1}}}},
{?eh,test_stats,{1,1,{0,1}}},
{?eh,tc_done,{repeat_1_SUITE,
@@ -291,7 +291,7 @@ test_events(repeat_seq) ->
{?eh,tc_done,{repeat_1_SUITE,
{end_per_group,gr_fail_result,[]},
{return_group_result,failed}}}],
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_2,repeat_seq_2},
{group_result,gr_fail_result,failed}}},
{?eh,test_stats,{4,2,{0,3}}},
{?eh,tc_done,{repeat_1_SUITE,
@@ -315,7 +315,7 @@ test_events(repeat_seq) ->
{failed,
{repeat_1_SUITE,init_per_group,
{'EXIT',fails_on_purpose}}}}}],
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_2,repeat_seq_3},
{group_result,gr_fail_init,failed}}},
{?eh,test_stats,{7,2,{0,6}}},
{?eh,tc_done,{repeat_1_SUITE,
@@ -329,12 +329,13 @@ test_events(repeat_seq) ->
[{?eh,tc_done,{repeat_1_SUITE,
{init_per_group,repeat_seq_4,[sequence,{repeat,2}]},
ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_1,'_'}},
{?eh,test_stats,{8,3,{0,8}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,
- tc_ok_1,{failed,{repeat_1_SUITE,tc_fail_1}}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_ok_1},
+ {failed,{repeat_1_SUITE,tc_fail_1}}}},
{?eh,test_stats,{8,3,{0,9}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,
- tc_ok_1,{failed,{repeat_1_SUITE,tc_fail_1}}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,repeat_seq_4},
+ {failed,{repeat_1_SUITE,tc_fail_1}}}},
{?eh,test_stats,{8,3,{0,10}}},
{?eh,tc_done,{repeat_1_SUITE,
{end_per_group,repeat_seq_4,[sequence,{repeat,2}]},
@@ -764,7 +765,7 @@ test_events(repeat_gr_until_any_fail) ->
{init_per_group,gr_ok_then_fail_init,[]},
{failed,{error,failing_this_time}}}},
{?eh,tc_auto_skip,
- {repeat_1_SUITE,tc_ok_1,
+ {repeat_1_SUITE,{tc_ok_1,gr_ok_then_fail_init},
{failed,
{repeat_1_SUITE,init_per_group,
{'EXIT',failing_this_time}}}}},
@@ -963,7 +964,7 @@ test_events(repeat_gr_until_all_ok) ->
[{?eh,tc_done,{repeat_1_SUITE,
{init_per_group,gr_fail_init_then_ok,[]},
{failed,{error,failing_this_time}}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_fail_init_then_ok},
{failed,{repeat_1_SUITE,init_per_group,
{'EXIT',failing_this_time}}}}},
{?eh,test_stats,{7,1,{0,1}}},
@@ -1237,10 +1238,10 @@ test_events(repeat_seq_until_any_fail) ->
{?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,
{failed,{error,failing_this_time}}}},
{?eh,test_stats,{15,1,{0,0}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_2,repeat_seq_until_any_fail_3},
{failed,{repeat_1_SUITE,tc_ok_then_fail_1}}}},
{?eh,test_stats,{15,1,{0,1}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_ok_1},
{failed,{repeat_1_SUITE,tc_ok_then_fail_1}}}},
{?eh,test_stats,{15,1,{0,2}}},
{?eh,tc_done,{repeat_1_SUITE,
@@ -1264,10 +1265,10 @@ test_events(repeat_seq_until_any_fail) ->
[{?eh,tc_done,{repeat_1_SUITE,
{end_per_group,gr_ok_then_fail_result,[]},
{return_group_result,failed}}}],
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_ok_1},
{group_result,gr_ok_then_fail_result,failed}}},
{?eh,test_stats,{19,1,{0,3}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,repeat_seq_until_any_fail_4},
{group_result,gr_ok_then_fail_result,failed}}},
{?eh,test_stats,{19,1,{0,4}}},
{?eh,tc_done,{repeat_1_SUITE,
@@ -1296,10 +1297,10 @@ test_events(repeat_seq_until_any_fail) ->
{?eh,tc_auto_skip,{repeat_1_SUITE,{end_per_group,gr_ok_then_fail_init},
{failed,{repeat_1_SUITE,init_per_group,
{'EXIT',failing_this_time}}}}}],
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,gr_ok_2},
{group_result,gr_ok_then_fail_init,failed}}},
{?eh,test_stats,{24,1,{0,6}}},
- {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {?eh,tc_auto_skip,{repeat_1_SUITE,{tc_ok_1,repeat_seq_until_any_fail_5},
{group_result,gr_ok_then_fail_init,failed}}},
{?eh,test_stats,{24,1,{0,7}}},
{?eh,tc_done,{repeat_1_SUITE,
diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
index bb2aba2c5a..b6f285322d 100644
--- a/lib/common_test/test/ct_repeat_testrun_SUITE.erl
+++ b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
@@ -343,9 +343,9 @@ skip_first_tc1(Suite) ->
{?eh,tc_done,{Suite,tc2,?skipped}},
{?eh,test_stats,{'_',0,{0,1}}},
{?eh,tc_done,{Suite,{init_per_group,g,[]},?skipped}},
- {?eh,tc_auto_skip,{Suite,tc1,?skip_reason}},
+ {?eh,tc_auto_skip,{Suite,{tc1,g},?skip_reason}},
{?eh,test_stats,{'_',0,{0,2}}},
- {?eh,tc_auto_skip,{Suite,tc2,?skip_reason}},
+ {?eh,tc_auto_skip,{Suite,{tc2,g},?skip_reason}},
{?eh,test_stats,{'_',0,{0,3}}},
{?eh,tc_auto_skip,{Suite,{end_per_group,g},?skip_reason}},
{?eh,tc_done,{Suite,tc2,?skipped}},
diff --git a/lib/common_test/test/ct_sequence_1_SUITE.erl b/lib/common_test/test/ct_sequence_1_SUITE.erl
index 8c87236838..5a775a1117 100644
--- a/lib/common_test/test/ct_sequence_1_SUITE.erl
+++ b/lib/common_test/test/ct_sequence_1_SUITE.erl
@@ -185,7 +185,8 @@ test_events(subgroup_return_fail) ->
{?eh,tc_done,{subgroups_1_SUITE,{end_per_group,return_fail,[]},
{return_group_result,failed}}}],
{?eh,tc_auto_skip,
- {subgroups_1_SUITE,ok_tc,{group_result,return_fail,failed}}},
+ {subgroups_1_SUITE,{ok_tc,ok_group},
+ {group_result,return_fail,failed}}},
{?eh,test_stats,{0,1,{0,1}}},
{?eh,tc_start,
{subgroups_1_SUITE,{end_per_group,subgroup_return_fail,[sequence]}}},
@@ -208,14 +209,15 @@ test_events(subgroup_init_fail) ->
[{?eh,tc_start,{subgroups_1_SUITE,{init_per_group,fail_init,[]}}},
{?eh,tc_done,{subgroups_1_SUITE,{init_per_group,fail_init,[]},
{failed,{error,init_per_group_fails_on_purpose}}}},
- {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,{ok_tc,fail_init},
{failed,{subgroups_1_SUITE,init_per_group,
{'EXIT',init_per_group_fails_on_purpose}}}}},
{?eh,test_stats,{0,0,{0,1}}},
{?eh,tc_auto_skip,{subgroups_1_SUITE,{end_per_group,fail_init},
{failed,{subgroups_1_SUITE,init_per_group,
{'EXIT',init_per_group_fails_on_purpose}}}}}],
- {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,{group_result,fail_init,failed}}},
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,{ok_tc,ok_group},
+ {group_result,fail_init,failed}}},
{?eh,test_stats,{0,0,{0,2}}},
{?eh,tc_start,{subgroups_1_SUITE,{end_per_group,subgroup_init_fail,[sequence]}}},
{?eh,tc_done,{subgroups_1_SUITE,
@@ -237,7 +239,8 @@ test_events(subgroup_after_failed_case) ->
{?eh,tc_start,{subgroups_1_SUITE,failing_tc}},
{?eh,tc_done,{subgroups_1_SUITE,failing_tc,{failed,{error,{{badmatch,3},'_'}}}}},
{?eh,test_stats,{0,1,{0,0}}},
- {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,{failed,{subgroups_1_SUITE,failing_tc}}}},
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,{ok_tc,ok_group},
+ {failed,{subgroups_1_SUITE,failing_tc}}}},
{?eh,test_stats,{0,1,{0,1}}},
{?eh,tc_start,{subgroups_1_SUITE,
{end_per_group,subgroup_after_failed_case,[sequence]}}},
@@ -265,7 +268,8 @@ test_events(case_after_subgroup_return_fail) ->
{?eh,tc_start,{subgroups_1_SUITE,{end_per_group,return_fail,[]}}},
{?eh,tc_done,{subgroups_1_SUITE,{end_per_group,return_fail,[]},
{return_group_result,failed}}}],
- {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,{group_result,return_fail,failed}}},
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,{ok_tc,case_after_subgroup_return_fail},
+ {group_result,return_fail,failed}}},
{?eh,test_stats,{0,1,{0,1}}},
{?eh,tc_start,{subgroups_1_SUITE,
{end_per_group,case_after_subgroup_return_fail,[sequence]}}},
@@ -289,7 +293,7 @@ test_events(case_after_subgroup_fail_init) ->
{?eh,tc_done,{subgroups_1_SUITE,
{init_per_group,fail_init,[]},
{failed,{error,init_per_group_fails_on_purpose}}}},
- {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,{ok_tc,fail_init},
{failed,
{subgroups_1_SUITE,init_per_group,
{'EXIT',init_per_group_fails_on_purpose}}}}},
@@ -300,7 +304,8 @@ test_events(case_after_subgroup_fail_init) ->
{'EXIT',init_per_group_fails_on_purpose}}}}}],
{?eh,tc_auto_skip,
- {subgroups_1_SUITE,ok_tc,{group_result,fail_init,failed}}},
+ {subgroups_1_SUITE,{ok_tc,case_after_subgroup_fail_init},
+ {group_result,fail_init,failed}}},
{?eh,test_stats,{0,0,{0,2}}},
{?eh,tc_start,{subgroups_1_SUITE,
{end_per_group,case_after_subgroup_fail_init,[sequence]}}},
diff --git a/lib/common_test/test/ct_skip_SUITE.erl b/lib/common_test/test/ct_skip_SUITE.erl
index b0a6c839a2..6fb803b928 100644
--- a/lib/common_test/test/ct_skip_SUITE.erl
+++ b/lib/common_test/test/ct_skip_SUITE.erl
@@ -153,10 +153,10 @@ testspec_skip(Config) when is_list(Config) ->
{skip_groups, TestDir, user_skip_6_SUITE, psub1, "SKIPPED"}],
{Opts,ERPid} = setup_testspec([{ts1,TestSpec1},
- {ts2,TestSpec2},
- {ts3,TestSpec3},
- {ts4,TestSpec4},
- {ts5,TestSpec5}], Config),
+ {ts2,TestSpec2},
+ {ts3,TestSpec3},
+ {ts4,TestSpec4},
+ {ts5,TestSpec5}], Config),
ok = ct_test_support:run(Opts, Config),
@@ -234,8 +234,8 @@ test_events(auto_skip) ->
{?eh,tc_done,
{auto_skip_2_SUITE,init_per_suite,{failed,{error,init_per_suite_failed}}}},
{?eh,tc_auto_skip,
- {auto_skip_2_SUITE,tc1,{failed,{auto_skip_2_SUITE,init_per_suite,
- {'EXIT',init_per_suite_failed}}}}},
+ {auto_skip_2_SUITE,{tc1,g1},{failed,{auto_skip_2_SUITE,init_per_suite,
+ {'EXIT',init_per_suite_failed}}}}},
{?eh,test_stats,{0,0,{0,3}}},
{?eh,tc_auto_skip,
{auto_skip_2_SUITE,end_per_suite,{failed,{auto_skip_2_SUITE,init_per_suite,
@@ -274,12 +274,12 @@ test_events(auto_skip) ->
{?eh,tc_done,
{auto_skip_5_SUITE,{init_per_group,g1,[]},{failed,{error,{group,g1,failed}}}}},
{?eh,tc_auto_skip,
- {auto_skip_5_SUITE,tc1,{failed,{auto_skip_5_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_5_SUITE,{tc1,g1},{failed,{auto_skip_5_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,6}}},
{?eh,tc_auto_skip,
- {auto_skip_5_SUITE,tc2,{failed,{auto_skip_5_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_5_SUITE,{tc2,g1},{failed,{auto_skip_5_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,7}}},
{?eh,tc_auto_skip,
{auto_skip_5_SUITE,{end_per_group,g1},
@@ -295,20 +295,20 @@ test_events(auto_skip) ->
{?eh,tc_done,
{auto_skip_6_SUITE,{init_per_group,g1,[]},{failed,{error,{group,g1,failed}}}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc1,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_6_SUITE,{tc1,g1},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,8}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc3,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_6_SUITE,{tc3,g2},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,9}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc4,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_6_SUITE,{tc4,g2},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,10}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc2,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g1,failed}}}}}},
+ {auto_skip_6_SUITE,{tc2,g1},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g1,failed}}}}}},
{?eh,test_stats,{2,0,{0,11}}},
{?eh,tc_auto_skip,
{auto_skip_6_SUITE,{end_per_group,g1},
@@ -324,12 +324,12 @@ test_events(auto_skip) ->
{?eh,tc_done,{auto_skip_6_SUITE,{init_per_group,g4,[]},
{failed,{error,{group,g4,failed}}}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc3,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g4,failed}}}}}},
+ {auto_skip_6_SUITE,{tc3,g4},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g4,failed}}}}}},
{?eh,test_stats,{3,0,{0,12}}},
{?eh,tc_auto_skip,
- {auto_skip_6_SUITE,tc4,{failed,{auto_skip_6_SUITE,init_per_group,
- {'EXIT',{group,g4,failed}}}}}},
+ {auto_skip_6_SUITE,{tc4,g4},{failed,{auto_skip_6_SUITE,init_per_group,
+ {'EXIT',{group,g4,failed}}}}}},
{?eh,test_stats,{3,0,{0,13}}},
{?eh,tc_auto_skip,
{auto_skip_6_SUITE,{end_per_group,g4},
@@ -498,13 +498,13 @@ test_events(auto_skip) ->
[{suite,auto_skip_12_SUITE}]},
{auto_skipped,
{require_failed,{not_available,unknown_variable_g1}}}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc1,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc1,g1},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,25}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc2,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc2,g1},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,26}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc3,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc3,g2},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,27}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g1},
@@ -516,13 +516,13 @@ test_events(auto_skip) ->
[{suite,auto_skip_12_SUITE}]},
{auto_skipped,
{require_failed,{not_available,unknown_variable_g1}}}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc1,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc1,g1},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,28}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc2,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc2,g1},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,29}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc3,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc3,g2},
{require_failed,{not_available,unknown_variable_g1}}}},
{?eh,test_stats,{10,0,{0,30}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g1},
@@ -544,7 +544,7 @@ test_events(auto_skip) ->
[{suite,auto_skip_12_SUITE}]},
{auto_skipped,
{require_failed,{not_available,unknown_variable_g4}}}}},
- {?eh,tc_auto_skip,{auto_skip_12_SUITE,tc3,
+ {?eh,tc_auto_skip,{auto_skip_12_SUITE,{tc3,g4},
{require_failed,{not_available,unknown_variable_g4}}}},
{?eh,test_stats,{12,0,{0,31}}},
{?eh,tc_auto_skip,{ct_framework,{end_per_group,g4},
@@ -574,10 +574,10 @@ test_events(user_skip) ->
{user_skip_1_SUITE,tc1,"Whole suite skipped"}},
{?eh,test_stats,{0,0,{1,0}}},
{?eh,tc_user_skip,
- {user_skip_1_SUITE,tc2,"Whole suite skipped"}},
+ {user_skip_1_SUITE,{tc2,g1},"Whole suite skipped"}},
{?eh,test_stats,{0,0,{2,0}}},
{?eh,tc_user_skip,
- {user_skip_1_SUITE,tc3,"Whole suite skipped"}},
+ {user_skip_1_SUITE,{tc3,g1},"Whole suite skipped"}},
{?eh,test_stats,{0,0,{3,0}}},
{?eh,tc_user_skip,
{user_skip_1_SUITE,tc4,"Whole suite skipped"}},
@@ -638,9 +638,9 @@ test_events(user_skip) ->
[{?eh,tc_start,{user_skip_4_SUITE,{init_per_group,g1,[]}}},
{?eh,tc_done,{user_skip_4_SUITE,{init_per_group,g1,[]},{skipped,"Group skipped"}}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc1,"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc1,g1},"Group skipped"}},
{?eh,test_stats,{3,0,{10,0}}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc2,"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc2,g1},"Group skipped"}},
{?eh,test_stats,{3,0,{11,0}}},
{?eh,tc_user_skip,{user_skip_4_SUITE,{end_per_group,g1},"Group skipped"}}],
@@ -657,10 +657,10 @@ test_events(user_skip) ->
[{?eh,tc_start,{user_skip_4_SUITE,{init_per_group,g3,[]}}},
{?eh,tc_done,{user_skip_4_SUITE,{init_per_group,g3,[]},{skipped,"Group skipped"}}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc5,"Group skipped"}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc6,"Group skipped"}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc7,"Group skipped"}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc8,"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc5,g3},"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc6,g4},"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc7,g4},"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc8,g3},"Group skipped"}},
{?eh,test_stats,{5,0,{15,0}}},
{?eh,tc_user_skip,{user_skip_4_SUITE,{end_per_group,g3},"Group skipped"}}],
@@ -671,9 +671,9 @@ test_events(user_skip) ->
{?eh,test_stats,{6,0,{15,0}}},
[{?eh,tc_start,{user_skip_4_SUITE,{init_per_group,g6,[]}}},
{?eh,tc_done,{user_skip_4_SUITE,{init_per_group,g6,[]},{skipped,"Group skipped"}}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc10,"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc10,g6},"Group skipped"}},
{?eh,test_stats,{6,0,{16,0}}},
- {?eh,tc_user_skip,{user_skip_4_SUITE,tc11,"Group skipped"}},
+ {?eh,tc_user_skip,{user_skip_4_SUITE,{tc11,g6},"Group skipped"}},
{?eh,test_stats,{6,0,{17,0}}},
{?eh,tc_user_skip,{user_skip_4_SUITE,{end_per_group,g6},"Group skipped"}}],
{?eh,tc_start,{user_skip_4_SUITE,{end_per_group,g5,[]}}},
@@ -687,9 +687,9 @@ test_events(user_skip) ->
{skipped,{bad,'Whole suite skipped'}}}},
{?eh,tc_user_skip,{user_skip_5_SUITE,tc1,{bad,'Whole suite skipped'}}},
{?eh,test_stats,{6,0,{18,0}}},
- {?eh,tc_user_skip,{user_skip_5_SUITE,tc2,{bad,'Whole suite skipped'}}},
+ {?eh,tc_user_skip,{user_skip_5_SUITE,{tc2,g1},{bad,'Whole suite skipped'}}},
{?eh,test_stats,{6,0,{19,0}}},
- {?eh,tc_user_skip,{user_skip_5_SUITE,tc3,{bad,'Whole suite skipped'}}},
+ {?eh,tc_user_skip,{user_skip_5_SUITE,{tc3,g1},{bad,'Whole suite skipped'}}},
{?eh,test_stats,{6,0,{20,0}}},
{?eh,tc_user_skip,{user_skip_5_SUITE,tc4,{bad,'Whole suite skipped'}}},
{?eh,test_stats,{6,0,{21,0}}},
@@ -700,10 +700,10 @@ test_events(user_skip) ->
{?eh,tc_done,{user_skip_6_SUITE,
{init_per_group,ptop1,[parallel]},
{skipped,"Top group skipped"}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc1,"Top group skipped"}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc3,"Top group skipped"}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc4,"Top group skipped"}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc2,"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc1,ptop1},"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc3,psub1},"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc4,psub1},"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc2,ptop1},"Top group skipped"}},
{?eh,tc_user_skip,{user_skip_6_SUITE,{end_per_group,ptop1},
"Top group skipped"}}]},
@@ -718,8 +718,8 @@ test_events(user_skip) ->
{?eh,tc_done,{user_skip_6_SUITE,
{init_per_group,psub2,[parallel]},
{skipped,"Sub group skipped"}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc3,"Sub group skipped"}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc4,"Sub group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc3,psub2},"Sub group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc4,psub2},"Sub group skipped"}},
{?eh,tc_user_skip,{user_skip_6_SUITE,{end_per_group,psub2},
"Sub group skipped"}}]},
@@ -745,14 +745,14 @@ test_events(testspec_skip) ->
{user_skip_7_SUITE,{init_per_group,ptop1,[parallel]}}},
{?eh,tc_done,
{user_skip_7_SUITE,{init_per_group,ptop1,[parallel]},ok}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc1,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc1,ptop1},"SKIPPED"}},
{?eh,test_stats,{0,0,{1,0}}},
{parallel,
[{?eh,tc_start,
{user_skip_7_SUITE,{init_per_group,psub1,[parallel]}}},
{?eh,tc_done,
{user_skip_7_SUITE,{init_per_group,psub1,[parallel]},ok}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc3,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc3,psub1},"SKIPPED"}},
{?eh,tc_start,{user_skip_7_SUITE,tc4}},
{?eh,tc_done,{user_skip_7_SUITE,tc4,ok}},
{?eh,test_stats,{1,0,{2,0}}},
@@ -778,13 +778,13 @@ test_events(testspec_skip) ->
{?eh,tc_start,{ct_framework,init_per_suite}},
{?eh,tc_done,{ct_framework,init_per_suite,ok}},
{?eh,tc_user_skip,{user_skip_7_SUITE,{init_per_group,ptop1},"SKIPPED"}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc1,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc1,ptop1},"SKIPPED"}},
{?eh,test_stats,{0,0,{1,0}}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc3,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc3,psub1},"SKIPPED"}},
{?eh,test_stats,{0,0,{2,0}}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc4,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc4,psub1},"SKIPPED"}},
{?eh,test_stats,{0,0,{3,0}}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc2,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc2,ptop1},"SKIPPED"}},
{?eh,test_stats,{0,0,{4,0}}},
{?eh,tc_user_skip,{user_skip_7_SUITE,{end_per_group,ptop1},"SKIPPED"}},
{?eh,tc_start,{ct_framework,end_per_suite}},
@@ -804,8 +804,8 @@ test_events(testspec_skip) ->
{user_skip_7_SUITE,{init_per_group,ptop1,[parallel]},ok}},
{?eh,tc_user_skip,
{user_skip_7_SUITE,{init_per_group,psub1},"SKIPPED"}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc3,"SKIPPED"}},
- {?eh,tc_user_skip,{user_skip_7_SUITE,tc4,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc3,psub1},"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_7_SUITE,{tc4,psub1},"SKIPPED"}},
{?eh,test_stats,{0,0,{2,0}}},
{?eh,tc_user_skip,{user_skip_7_SUITE,{end_per_group,psub1},"SKIPPED"}},
{?eh,tc_start,{user_skip_7_SUITE,tc1}},
@@ -837,13 +837,13 @@ test_events(testspec_skip) ->
{?eh,tc_done,{user_skip_6_SUITE,
{init_per_group,ptop1,[parallel]},
{skipped,"Top group skipped"}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc1,"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc1,ptop1},"Top group skipped"}},
{?eh,test_stats,{0,0,{1,0}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc3,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc3,psub1},"SKIPPED"}},
{?eh,test_stats,{0,0,{2,0}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc4,"SKIPPED"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc4,psub1},"SKIPPED"}},
{?eh,test_stats,{0,0,{3,0}}},
- {?eh,tc_user_skip,{user_skip_6_SUITE,tc2,"Top group skipped"}},
+ {?eh,tc_user_skip,{user_skip_6_SUITE,{tc2,ptop1},"Top group skipped"}},
{?eh,test_stats,{0,0,{4,0}}},
{?eh,tc_user_skip,
{user_skip_6_SUITE,{end_per_group,ptop1},"Top group skipped"}}]},
diff --git a/lib/common_test/test/ct_surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE.erl
index c5e44682b0..db7a0be915 100644
--- a/lib/common_test/test/ct_surefire_SUITE.erl
+++ b/lib/common_test/test/ct_surefire_SUITE.erl
@@ -205,7 +205,7 @@ test_events(_) ->
[{?eh,tc_start,{surefire_SUITE,{init_per_group,g_fail,[]}}},
{?eh,tc_done,{surefire_SUITE,{init_per_group,g_fail,[]},
{failed,{error,all_cases_should_be_skipped}}}},
- {?eh,tc_auto_skip,{surefire_SUITE,tc_ok,
+ {?eh,tc_auto_skip,{surefire_SUITE,{tc_ok,g_fail},
{failed,
{surefire_SUITE,init_per_group,
{'EXIT',all_cases_should_be_skipped}}}}},
diff --git a/lib/common_test/test/ct_telnet_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE.erl
index acce4eca14..84e69c2b54 100644
--- a/lib/common_test/test/ct_telnet_SUITE.erl
+++ b/lib/common_test/test/ct_telnet_SUITE.erl
@@ -72,19 +72,32 @@ init_per_suite(Config) ->
end_per_suite(Config) ->
ct_test_support:end_per_suite(Config).
-init_per_testcase(TestCase, Config) when TestCase=/=unix_telnet->
+init_per_testcase(TestCase, Config) when TestCase /= unix_telnet ->
+ ct:pal("Testcase ~p starting!", [TestCase]),
TS = telnet_server:start([{port,?erl_telnet_server_port},
{users,[{?erl_telnet_server_user,
?erl_telnet_server_pwd}]}]),
ct_test_support:init_per_testcase(TestCase, [{telnet_server,TS}|Config]);
init_per_testcase(TestCase, Config) ->
- ct_test_support:init_per_testcase(TestCase, Config).
-
+ ct:pal("Testcase ~p starting. Checking connection to telnet server...",
+ [TestCase]),
+ ct:require(testconn, {unix,[telnet]}),
+ case {os:type(),ct_telnet:open(testconn)} of
+ {_,{ok,Handle}} ->
+ ok = ct_telnet:close(Handle),
+ ct:pal("Connection ok, starting tests!", []),
+ ct_test_support:init_per_testcase(TestCase, Config);
+ {{unix,_},{error,Reason}} ->
+ ct:fail("No connection to telnet server! Reason: ~tp", [Reason]);
+ {_,{error,Reason}} ->
+ {skip,{no_access_to_telnet_server,Reason}}
+ end.
+
+end_per_testcase(TestCase, Config) when TestCase /= unix_telnet ->
+ ct:pal("Stopping the telnet_server now!", []),
+ telnet_server:stop(?config(telnet_server,Config)),
+ ct_test_support:end_per_testcase(TestCase, Config);
end_per_testcase(TestCase, Config) ->
- case ?config(telnet_server,Config) of
- undefined -> ok;
- TS -> telnet_server:stop(TS)
- end,
ct_test_support:end_per_testcase(TestCase, Config).
@@ -167,28 +180,39 @@ telnet_config(unix_telnet, legacy) ->
{ct_conn_log,[]}];
%% LogType same as GroupName
telnet_config(unix_telnet, LogType) ->
+ LogTypeTerm = if LogType == raw -> [];
+ true -> [{log_type,LogType}]
+ end,
[{unix, ct:get_config(unix)},
{ct_conn_log,
- [{ct_telnet,[{log_type,LogType},
- {hosts,[telnet_server_conn1,
- telnet_server_conn2,
- telnet_server_conn3,
- telnet_server_conn4]}]}]}];
+ [{ct_telnet, LogTypeTerm ++
+ [{hosts,[telnet_server_conn1,
+ telnet_server_conn2,
+ telnet_server_conn3,
+ telnet_server_conn4]}]}]}];
telnet_config(_, LogType) ->
+ LogTypeTerm = if LogType == raw -> [];
+ true -> [{log_type,LogType}]
+ end,
[{unix,[{telnet,"localhost"},
{port, ?erl_telnet_server_port},
{username,?erl_telnet_server_user},
{password,?erl_telnet_server_pwd},
- {keep_alive,true}]} |
+ {keep_alive,true}]},
+ {telnet_settings, [{connect_timeout,10000},
+ {command_timeout,10000},
+ {reconnection_attempts,0},
+ {reconnection_interval,0},
+ {keep_alive,true}]} |
if LogType == legacy ->
[{ct_conn_log,[]}];
true ->
[{ct_conn_log,
- [{ct_telnet,[{log_type,LogType},
- {hosts,[telnet_server_conn1,
- telnet_server_conn2,
- telnet_server_conn3,
- telnet_server_conn4]}]}]}]
+ [{ct_telnet, LogTypeTerm ++
+ [{hosts,[telnet_server_conn1,
+ telnet_server_conn2,
+ telnet_server_conn3,
+ telnet_server_conn4]}]}]}]
end].
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl
index 8d142e85a8..c0f79d0f10 100644
--- a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl
+++ b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl
@@ -16,11 +16,14 @@ suite() ->
].
all() ->
- [expect,
+ [
+ expect,
expect_repeat,
expect_sequence,
expect_error_prompt,
- expect_error_timeout,
+ expect_error_timeout1,
+ expect_error_timeout2,
+ expect_error_timeout3,
no_prompt_check,
no_prompt_check_repeat,
no_prompt_check_sequence,
@@ -28,7 +31,11 @@ all() ->
ignore_prompt,
ignore_prompt_repeat,
ignore_prompt_sequence,
- ignore_prompt_timeout].
+ ignore_prompt_timeout,
+ large_string,
+ server_speaks,
+ server_disconnects
+ ].
groups() ->
[].
@@ -85,13 +92,34 @@ expect_error_prompt(_) ->
%% Check that expect returns after idle timeout, and even if the
%% expected pattern is received - as long as not newline or prompt is
%% received it will not match.
-expect_error_timeout(_) ->
+expect_error_timeout1(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
ok = ct_telnet:send(Handle, "echo_no_prompt xxx"),
{error,timeout} = ct_telnet:expect(Handle, ["xxx"], [{timeout,1000}]),
ok = ct_telnet:close(Handle),
ok.
+expect_error_timeout2(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ ok = ct_telnet:send(Handle, "echo_no_prompt xxx"),
+ {error,timeout} = ct_telnet:expect(Handle, ["xxx"], [{idle_timeout,1000},
+ {total_timeout,infinity}]),
+ ok = ct_telnet:close(Handle),
+ ok.
+
+%% Check that if server loops and pattern not matching, the operation
+%% can be aborted
+expect_error_timeout3(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ ok = ct_telnet:send(Handle, "echo_loop 5000 xxx"),
+ {error,timeout} = ct_telnet:expect(Handle, ["yyy"],
+ [{idle_timeout,infinity},
+ {total_timeout,3000}]),
+ ok = ct_telnet:send(Handle, "echo ayt"),
+ {ok,["ayt"]} = ct_telnet:expect(Handle, ["ayt"]),
+ ok = ct_telnet:close(Handle),
+ ok.
+
%% expect with ignore_prompt option should not return even if a prompt
%% is found. The pattern after the prompt (here "> ") can be matched.
ignore_prompt(_) ->
@@ -188,3 +216,72 @@ no_prompt_check_timeout(_) ->
{timeout,1000}]),
ok = ct_telnet:close(Handle),
ok.
+
+%% Check that it's possible to receive multiple chunks of data sent from
+%% the server with one get_data call
+large_string(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ String = "abcd efgh ijkl mnop qrst uvwx yz ",
+ BigString = lists:flatmap(fun(S) -> S end,
+ [String || _ <- lists:seq(1,10)]),
+ VerifyStr = [C || C <- BigString, C/=$ ],
+
+ {ok,Data} = ct_telnet:cmd(Handle, "echo_sep "++BigString),
+ ct:log("[CMD] Received ~w chars: ~s", [length(lists:flatten(Data)),Data]),
+ VerifyStr = [C || C <- lists:flatten(Data), C/=$ , C/=$\r, C/=$\n, C/=$>],
+
+ %% Test #1: With a long sleep value, all data gets gets buffered and
+ %% ct_telnet can receive it with one single request to ct_telnet_client.
+ %% Test #2: With a short sleep value, ct_telnet needs multiple calls to
+ %% ct_telnet_client to collect the data. This iterative operation should
+ %% yield the same result as the single request case.
+
+ ok = ct_telnet:send(Handle, "echo_sep "++BigString),
+ timer:sleep(1000),
+ {ok,Data1} = ct_telnet:get_data(Handle),
+ ct:log("[GET DATA #1] Received ~w chars: ~s",
+ [length(lists:flatten(Data1)),Data1]),
+ VerifyStr = [C || C <- lists:flatten(Data1), C/=$ , C/=$\r, C/=$\n, C/=$>],
+
+ ok = ct_telnet:send(Handle, "echo_sep "++BigString),
+ timer:sleep(50),
+ {ok,Data2} = ct_telnet:get_data(Handle),
+ ct:log("[GET DATA #2] Received ~w chars: ~s", [length(lists:flatten(Data2)),Data2]),
+ VerifyStr = [C || C <- lists:flatten(Data2), C/=$ , C/=$\r, C/=$\n, C/=$>],
+
+ ok = ct_telnet:close(Handle),
+ ok.
+
+%% The server says things. Manually check that it gets printed correctly
+%% in the general IO log.
+server_speaks(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ ok = ct_telnet:send(Handle, "echo_no_prompt This is the first message\r\n"),
+ ok = ct_telnet:send(Handle, "echo_no_prompt This is the second message\r\n"),
+ %% let ct_telnet_client get an idle timeout
+ timer:sleep(15000),
+ ok = ct_telnet:send(Handle, "echo_no_prompt This is the third message\r\n"),
+ {ok,_} = ct_telnet:expect(Handle, ["the"], [no_prompt_check]),
+ {error,timeout} = ct_telnet:expect(Handle, ["the"], [no_prompt_check,
+ {timeout,1000}]),
+ ok = ct_telnet:send(Handle, "echo_no_prompt This is the fourth message\r\n"),
+ %% give the server time to respond
+ timer:sleep(2000),
+ %% closing the connection should print last message in log
+ ok = ct_telnet:close(Handle),
+ ok.
+
+%% Let the server close the connection. Make sure buffered data gets printed
+%% to the general IO log.
+server_disconnects(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ ok = ct_telnet:send(Handle, "disconnect_after 1500"),
+ %% wait until the get_data operation (triggered by send/2) times out
+ %% before sending the msg
+ timer:sleep(500),
+ ok = ct_telnet:send(Handle, "echo_no_prompt This is the message\r\n"),
+ %% when the server closes the connection, the last message should be
+ %% printed in the log
+ timer:sleep(3000),
+ _ = ct_telnet:close(Handle),
+ ok.
diff --git a/lib/common_test/test/ct_test_server_if_1_SUITE.erl b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
index 9882fa980c..b6ef3062d4 100644
--- a/lib/common_test/test/ct_test_server_if_1_SUITE.erl
+++ b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
@@ -168,7 +168,7 @@ test_events(ts_if_1) ->
{?eh,tc_start,{ts_if_1_SUITE,tc4}},
{?eh,tc_done,{ts_if_1_SUITE,tc4,{failed,{error,failed_on_purpose}}}},
{?eh,test_stats,{1,3,{0,2}}},
- {?eh,tc_auto_skip,{ts_if_1_SUITE,tc5,{failed,{ts_if_1_SUITE,tc4}}}},
+ {?eh,tc_auto_skip,{ts_if_1_SUITE,{tc5,seq2},{failed,{ts_if_1_SUITE,tc4}}}},
{?eh,test_stats,{1,3,{0,3}}},
{?eh,tc_start,{ts_if_1_SUITE,{end_per_group,seq2,[sequence]}}},
{?eh,tc_done,{ts_if_1_SUITE,{end_per_group,seq2,[sequence]},ok}}],
@@ -199,7 +199,7 @@ test_events(ts_if_1) ->
[{?eh,tc_start,{ts_if_1_SUITE,{init_per_group,g1,[]}}},
{?eh,tc_done,{ts_if_1_SUITE,{init_per_group,g1,[]},
{skipped,g1_got_skipped}}},
- {?eh,tc_user_skip,{ts_if_1_SUITE,gtc1,g1_got_skipped}},
+ {?eh,tc_user_skip,{ts_if_1_SUITE,{gtc1,g1},g1_got_skipped}},
{?eh,test_stats,{1,4,{3,6}}},
{?eh,tc_user_skip,{ts_if_1_SUITE,{end_per_group,g1},g1_got_skipped}}],
@@ -208,7 +208,7 @@ test_events(ts_if_1) ->
{?eh,tc_done,{ts_if_1_SUITE,{init_per_group,g2,[parallel]},ok}},
[{?eh,tc_start,{ts_if_1_SUITE,{init_per_group,g3,[]}}},
{?eh,tc_done,{ts_if_1_SUITE,{init_per_group,g3,[]},{skipped,g3_got_skipped}}},
- {?eh,tc_user_skip,{ts_if_1_SUITE,gtc2,g3_got_skipped}},
+ {?eh,tc_user_skip,{ts_if_1_SUITE,{gtc2,g3},g3_got_skipped}},
{?eh,test_stats,{1,4,{4,6}}},
{?eh,tc_user_skip,{ts_if_1_SUITE,{end_per_group,g3},g3_got_skipped}}],
{?eh,tc_start,{ts_if_1_SUITE,{end_per_group,g2,[parallel]}}},
@@ -279,7 +279,7 @@ test_events(ts_if_1) ->
{init_per_group,g1,[]},
{auto_skipped,{group0_failed,bad_return_value}}}},
{?eh,tc_auto_skip,
- {ts_if_7_SUITE,tc2,{group0_failed,bad_return_value}}},
+ {ts_if_7_SUITE,{tc2,g1},{group0_failed,bad_return_value}}},
{?eh,test_stats,{2,7,{4,11}}},
{?eh,tc_auto_skip,
{ts_if_7_SUITE,{end_per_group,g1},{group0_failed,bad_return_value}}},
diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl
index 187b5e6d3a..c2670316b6 100644
--- a/lib/common_test/test/ct_testspec_1_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_1_SUITE.erl
@@ -763,35 +763,35 @@ test_events(skip_all_groups) ->
{?eh,start_info,{1,1,12}},
{?eh,tc_start,{groups_11_SUITE,init_per_suite}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1a},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1a},"SKIPPED!"}},
{?eh,test_stats,{0,0,{1,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1a},"SKIPPED!"}},
{?eh,test_stats,{0,0,{2,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1a},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1b},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1b},"SKIPPED!"}},
{?eh,test_stats,{0,0,{3,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1b},"SKIPPED!"}},
{?eh,test_stats,{0,0,{4,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1b},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_2},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_2a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_2a,test_group_2},"SKIPPED!"}},
{?eh,test_stats,{0,0,{5,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_3a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_3a,test_group_3},"SKIPPED!"}},
{?eh,test_stats,{0,0,{6,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_3b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_3b,test_group_3},"SKIPPED!"}},
{?eh,test_stats,{0,0,{7,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_2b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_2b,test_group_2},"SKIPPED!"}},
{?eh,test_stats,{0,0,{8,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_2},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_4},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_5a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_5a,test_group_5},"SKIPPED!"}},
{?eh,test_stats,{0,0,{9,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_7a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_7a,test_group_7},"SKIPPED!"}},
{?eh,test_stats,{0,0,{10,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_7b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_7b,test_group_7},"SKIPPED!"}},
{?eh,test_stats,{0,0,{11,0}}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_5b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_5b,test_group_5},"SKIPPED!"}},
{?eh,test_stats,{0,0,{12,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_4},"SKIPPED!"}},
{?eh,tc_start,{groups_11_SUITE,end_per_suite}},
@@ -826,17 +826,17 @@ test_events(skip_group) ->
{?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1a,[]},ok}}],
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1b},
"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1b},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1b},"SKIPPED!"}},
{?eh,test_stats,{2,0,{2,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1b},
"SKIPPED!"}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_2},
"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_2a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_3a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_3b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_2b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_2a,test_group_2},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_3a,test_group_3},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_3b,test_group_3},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_2b,test_group_2},"SKIPPED!"}},
{?eh,test_stats,{2,0,{6,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_2},
"SKIPPED!"}},
@@ -864,15 +864,15 @@ test_events(skip_group_all_testcases) ->
{?eh,tc_start,{groups_11_SUITE,init_per_suite}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1a},
"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1a},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1a},"SKIPPED!"}},
{?eh,test_stats,{0,0,{2,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1a},
"SKIPPED!"}},
{?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1b},
"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1b},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1b},"SKIPPED!"}},
{?eh,test_stats,{0,0,{4,0}}},
{?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1b},
"SKIPPED!"}},
@@ -901,13 +901,13 @@ test_events(skip_group_testcase) ->
{?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_1a,[]}}},
{?eh,tc_start,{groups_11_SUITE,testcase_1a}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1a},"SKIPPED!"}},
{?eh,test_stats,{1,0,{1,0}}},
{?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1a,[]},'_'}},
{?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_1b,[]}}},
{?eh,tc_start,{groups_11_SUITE,testcase_1b}},
- {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1b},"SKIPPED!"}},
{?eh,test_stats,{2,0,{2,0}}},
{?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1b,[]},'_'}},
@@ -1045,8 +1045,8 @@ test_events(skip_subgroup) ->
{?eh,tc_user_skip,{groups_12_SUITE,
{init_per_group,test_group_8},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8a,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8b,test_group_8},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_12_SUITE,
{end_per_group,test_group_8},"SKIPPED!"}},
@@ -1146,12 +1146,12 @@ test_events(skip_subgroup_all_testcases) ->
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_4,[]},ok}},
{?eh,tc_user_skip,{groups_12_SUITE,
{init_per_group,test_group_5},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5a,test_group_5},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7a,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7b,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8a,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8b,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5b,test_group_5},"SKIPPED!"}},
{?eh,test_stats,{0,0,{6,0}}},
{?eh,tc_user_skip,{groups_12_SUITE,
{end_per_group,test_group_5},"SKIPPED!"}},
@@ -1240,9 +1240,9 @@ test_events(skip_subgroup_testcase) ->
{?eh,tc_done,
{groups_12_SUITE,{init_per_group,test_group_6,[parallel]},ok}},
[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_7,'_'}}},
- {?eh,tc_user_skip, {groups_12_SUITE,testcase_7a,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7a,test_group_7},"SKIPPED!"}},
{?eh,test_stats,{1,0,{1,0}}},
- {?eh,tc_user_skip, {groups_12_SUITE,testcase_7b,"SKIPPED!"}},
+ {?eh,tc_user_skip, {groups_12_SUITE,{testcase_7b,test_group_7},"SKIPPED!"}},
{?eh,test_stats,{1,0,{2,0}}},
{?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_7,'_'}}}],
{shuffle,
@@ -1281,22 +1281,22 @@ test_events(sub_skipped_by_top) ->
{?eh,tc_start,{groups_12_SUITE,init_per_suite}},
{?eh,tc_user_skip,{groups_12_SUITE,{init_per_group,test_group_4},
"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5a,test_group_5},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7a,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7b,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8a,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8b,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5b,test_group_5},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_12_SUITE,
{end_per_group,test_group_4},"SKIPPED!"}},
{?eh,tc_user_skip,{groups_12_SUITE,
{init_per_group,test_group_4},"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_7b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8a,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_8b,"SKIPPED!"}},
- {?eh,tc_user_skip,{groups_12_SUITE,testcase_5b,"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5a,test_group_5},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7a,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_7b,test_group_7},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8a,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_8b,test_group_8},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{testcase_5b,test_group_5},"SKIPPED!"}},
{?eh,test_stats,{0,0,{12,0}}},
{?eh,tc_user_skip,{groups_12_SUITE,
{end_per_group,test_group_4},"SKIPPED!"}},
diff --git a/lib/common_test/test/telnet_server.erl b/lib/common_test/test/telnet_server.erl
index 1760100d8e..1d341d6106 100644
--- a/lib/common_test/test/telnet_server.erl
+++ b/lib/common_test/test/telnet_server.erl
@@ -51,32 +51,51 @@ stop(Pid) ->
init(Opts) ->
Port = proplists:get_value(port,Opts),
Users = proplists:get_value(users,Opts,[]),
- {ok, LSock} = gen_tcp:listen(Port, [list, {packet, 0},
- {active, true}]),
+ {ok, LSock} = listen(5, Port, [list, {packet, 0},
+ {active, true},
+ {reuseaddr,true}]),
State = #state{listen=LSock,users=Users},
accept(State),
- ok = gen_tcp:close(LSock).
+ ok = gen_tcp:close(LSock),
+ dbg("telnet_server closed the listen socket ~p\n", [LSock]),
+ timer:sleep(1000),
+ ok.
+
+listen(0, _Port, _Opts) ->
+ {error,eaddrinuse};
+listen(Retries, Port, Opts) ->
+ case gen_tcp:listen(Port, Opts) of
+ {error,eaddrinuse} ->
+ dbg("Listen port not released, trying again..."),
+ timer:sleep(5000),
+ listen(Retries-1, Port, Opts);
+ Ok = {ok,_LSock} ->
+ Ok;
+ Error ->
+ exit(Error)
+ end.
accept(#state{listen=LSock}=State) ->
Server = self(),
Acceptor = spawn_link(fun() -> do_accept(LSock,Server) end),
receive
{Acceptor,Sock} when is_port(Sock) ->
+ dbg("Connected to client on socket ~p\n", [Sock]),
case init_client(State#state{client=Sock}) of
stopped ->
- io:format("[telnet_server] telnet_server stopped\n"),
+ dbg("telnet_server stopped\n"),
ok;
R ->
- io:format("[telnet_server] connection to client"
- "closed with reason ~p~n",[R]),
+ dbg("Connection to client "
+ "closed with reason ~p~n",[R]),
accept(State)
end;
{Acceptor,closed} ->
- io:format("[telnet_server] listen socket closed unexpectedly, "
- "terminating telnet_server\n"),
+ dbg("Listen socket closed unexpectedly, "
+ "terminating telnet_server\n"),
ok;
stop ->
- io:format("[telnet_server] telnet_server stopped\n"),
+ dbg("telnet_server stopped\n"),
ok
end.
@@ -97,19 +116,21 @@ init_client(#state{client=Sock}=State) ->
dbg("Server sending: ~p~n",["login: "]),
R = case gen_tcp:send(Sock,"login: ") of
ok ->
- loop(State);
+ loop(State, 1);
Error ->
Error
end,
_ = gen_tcp:close(Sock),
R.
-loop(State) ->
+loop(State, N) ->
receive
{tcp,_,Data} ->
try handle_data(Data,State) of
{ok,State1} ->
- loop(State1)
+ loop(State1, N);
+ closed ->
+ closed
catch
throw:Error ->
Error
@@ -118,6 +139,11 @@ loop(State) ->
closed;
{tcp_error,_,Error} ->
{error,tcp,Error};
+ disconnect ->
+ Sock = State#state.client,
+ dbg("Server closing connection on socket ~p~n", [Sock]),
+ ok = gen_tcp:close(Sock),
+ closed;
stop ->
stopped
end.
@@ -130,10 +156,16 @@ handle_data(Data,State) ->
case get_line(Data,[]) of
{Line,Rest} ->
WholeLine = lists:flatten(lists:reverse(State#state.buffer,Line)),
- {ok,State1} = do_handle_data(WholeLine,State),
- case Rest of
- [] -> {ok,State1};
- _ -> handle_data(Rest,State1)
+ case do_handle_data(WholeLine,State) of
+ {ok,State1} ->
+ case Rest of
+ [] -> {ok,State1};
+ _ -> handle_data(Rest,State1)
+ end;
+ {close,State1} ->
+ dbg("Server closing connection~n",[]),
+ gen_tcp:close(State1#state.client),
+ closed
end;
false ->
{ok,State#state{buffer=[Data|State#state.buffer]}}
@@ -163,22 +195,42 @@ do_handle_data(Data,#state{authorized=false}=State) ->
check_user(Data,State);
do_handle_data(Data,#state{authorized={user,_}}=State) ->
check_pwd(Data,State);
-do_handle_data("echo "++ Data,State) ->
+do_handle_data("echo " ++ Data,State) ->
send(Data++"\r\n> ",State),
{ok,State};
-do_handle_data("echo_no_prompt "++ Data,State) ->
+do_handle_data("echo_sep " ++ Data,State) ->
+ Msgs = string:tokens(Data," "),
+ lists:foreach(fun(Msg) ->
+ send(Msg,State),
+ timer:sleep(10)
+ end, Msgs),
+ send("\r\n> ",State),
+ {ok,State};
+do_handle_data("echo_no_prompt " ++ Data,State) ->
send(Data,State),
{ok,State};
-do_handle_data("echo_ml "++ Data,State) ->
+do_handle_data("echo_ml " ++ Data,State) ->
Lines = string:tokens(Data," "),
ReturnData = string:join(Lines,"\n"),
send(ReturnData++"\r\n> ",State),
{ok,State};
-do_handle_data("echo_ml_no_prompt "++ Data,State) ->
+do_handle_data("echo_ml_no_prompt " ++ Data,State) ->
Lines = string:tokens(Data," "),
ReturnData = string:join(Lines,"\n"),
send(ReturnData,State),
{ok,State};
+do_handle_data("echo_loop " ++ Data,State) ->
+ [TStr|Lines] = string:tokens(Data," "),
+ ReturnData = string:join(Lines,"\n"),
+ send_loop(list_to_integer(TStr),ReturnData,State),
+ {ok,State};
+do_handle_data("disconnect_after " ++WaitStr,State) ->
+ Wait = list_to_integer(string:strip(WaitStr,right,$\n)),
+ dbg("Server will close connection in ~w ms...", [Wait]),
+ erlang:send_after(Wait,self(),disconnect),
+ {ok,State};
+do_handle_data("disconnect" ++_,State) ->
+ {close,State};
do_handle_data([],State) ->
send("> ",State),
{ok,State};
@@ -212,6 +264,20 @@ send(Data,State) ->
throw({error,send,Error})
end.
+send_loop(T,Data,State) ->
+ dbg("Server sending ~p in loop for ~w ms...~n",[Data,T]),
+ send_loop(now(),T,Data,State).
+
+send_loop(T0,T,Data,State) ->
+ ElapsedMS = trunc(timer:now_diff(now(),T0)/1000),
+ if ElapsedMS >= T ->
+ ok;
+ true ->
+ send(Data,State),
+ timer:sleep(500),
+ send_loop(T0,T,Data,State)
+ end.
+
get_line([$\r,$\n|Rest],Acc) ->
{lists:reverse(Acc),Rest};
get_line([$\r,0|Rest],Acc) ->
@@ -226,4 +292,4 @@ get_line([],_) ->
dbg(_F) ->
dbg(_F,[]).
dbg(_F,_A) ->
- io:format("[telnet_server] "++_F,_A).
+ io:format("[telnet_server] " ++ _F,_A).
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index 568405b110..f8a5aab686 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.7.4
+COMMON_TEST_VSN = 1.8
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index c66c8ea4bf..5fccdcdcb5 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -551,6 +551,14 @@ module.beam: module.erl \
<c>{Module,Name,Arity}</c> or a list of such tuples.</p>
</item>
+ <tag><c>nowarn_deprecated_type</c></tag>
+ <item>
+ <p>Turns off warnings for uses of deprecated types. By
+ default (<c>warn_deprecated_type</c>), warnings are
+ emitted for every use of a type known by the compiler
+ to be deprecated.</p>
+ </item>
+
<tag><c>warn_obsolete_guard</c></tag>
<item>
<p>Causes warnings to be emitted for calls to old type
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index cb39b28d3d..a0f2e617cb 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -31,6 +31,233 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 5.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Line numbers would not be correct when a binary
+ construction such as
+ '<c>&lt;&lt;Bin/binary,...&gt;&gt;</c>' fails. (Thanks to
+ Stanislav Seletskiy for reporting this bug.)</p>
+ <p>
+ Own Id: OTP-11572</p>
+ </item>
+ <item>
+ <p>
+ The compiler now properly annotates the code in value in
+ the '<c>after</c>' clause for a '<c>try</c>' so that
+ Dialyzer no longer generates a false warning for an
+ unmatched return.</p>
+ <p>
+ Own Id: OTP-11580</p>
+ </item>
+ <item>
+ <p>
+ Some case statements where no clause would match could
+ cause an internal error in the compiler. (Thanks to Erik
+ Soe Sorensen for reporting this bug.)</p>
+ <p>
+ Own Id: OTP-11610</p>
+ </item>
+ <item>
+ <p>
+ With <c>--Wunmatched_returns</c>, dialyzer will no longer
+ warn when the value of a list comprehension is ignored,
+ provided that the each value in the list would be an
+ atomic value (such as integer or atoms, as opposed to
+ tuples and lists). Example: ignoring '<c>[io:format(...)
+ || ...]</c>' will not cause a warning, while ignoring
+ '<c>[file:close(Fd) || ...]</c>' will.</p>
+ <p>
+ Own Id: OTP-11626</p>
+ </item>
+ <item>
+ <p>
+ Matching out a binary and applying the binary as if it
+ were a fun would crash the run-time system. (Thanks to
+ Loïc Hoguin.)</p>
+ <p>
+ Own Id: OTP-11672</p>
+ </item>
+ <item>
+ <p>
+ Some local implementations of removing the last element
+ from a list are replaced by <c>lists:droplast/1</c>. Note
+ that this requires at least <c>stdlib-2.0</c>, which is
+ the stdlib version delivered in OTP 17.0. (Thanks to Hans
+ Svensson)</p>
+ <p>
+ Own Id: OTP-11678</p>
+ </item>
+ <item>
+ <p>
+ Allow all auto imports to be suppressed at once.
+ Introducing the no_auto_import attribute:
+ -compile(no_auto_import). Useful for code generation
+ tools that always use the qualified function names and
+ want to avoid the auto imported functions clashing with
+ local ones. (Thanks to José Valim.)</p>
+ <p>
+ Own Id: OTP-11682</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ <item>
+ <p>
+ Adapt 'asm' deprecation message to new version scheme.
+ (Thanks to Tuncer Ayaz)</p>
+ <p>
+ Own Id: OTP-11751</p>
+ </item>
+ <item>
+ <p>
+ A number of compiler errors where unusual or nonsensical
+ code would crash the compiler have been reported by Ulf
+ Norell and corrected by Anthony Ramine.</p>
+ <p>
+ Own Id: OTP-11770</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Compilation times for modules with a huge number for
+ record accesses using the dot operator has been improved.</p>
+ <p>
+ Own Id: OTP-10652</p>
+ </item>
+ <item>
+ <p>
+ The compiler can generate somewhat better code by moving
+ let expressions into sequences. (Thanks to Anthony
+ Ramine.)</p>
+ <p>
+ Own Id: OTP-11056</p>
+ </item>
+ <item>
+ <p>
+ Forbid unsized fields in patterns of binary generators
+ and simplified v3_core's translation of bit string
+ generators. (Thanks to Anthony Ramine.)</p>
+ <p>
+ Own Id: OTP-11186</p>
+ </item>
+ <item>
+ <p>
+ Funs can now be a given a name. Thanks to to Richard
+ O'Keefe for the idea (EEP37) and to Anthony Ramine for
+ the implementation.</p>
+ <p>
+ Own Id: OTP-11537</p>
+ </item>
+ <item>
+ <p>
+ Using the <c>from_asm</c> option to produce a BEAM file
+ starting from BEAM assembly code would often fail because
+ early optimization passes would not understand
+ instructions that later optimization passes would
+ introduce. (Thanks to Anthony Ramine.)</p>
+ <p>
+ Own Id: OTP-11544</p>
+ </item>
+ <item>
+ <p>
+ The <c>.core</c> and <c>.S</c> extensions are now
+ documented in the <c>erlc</c> documentation, and the
+ '<c>from_core</c>' and '<c>from_asm</c>' options are now
+ documented in the compiler documentation. (Thanks to
+ Tuncer Ayaz.)</p>
+ <p>
+ Own Id: OTP-11547</p>
+ </item>
+ <item>
+ <p>Optimization of case expressions that build tuples or
+ lists have been improved.</p>
+ <p>
+ Own Id: OTP-11584</p>
+ </item>
+ <item>
+ <p>
+ EEP43: New data type - Maps</p>
+ <p>
+ With Maps you may for instance: <taglist> <item><c>M0 =
+ #{ a =&gt; 1, b =&gt; 2}, % create
+ associations</c></item> <item><c>M1 = M0#{ a := 10 }, %
+ update values</c></item> <item><c>M2 = M1#{ "hi" =&gt;
+ "hello"}, % add new associations</c></item> <item><c>#{
+ "hi" := V1, a := V2, b := V3} = M2. % match keys with
+ values</c></item> </taglist></p>
+ <p>
+ For information on how to use Maps please see the
+ <seealso marker="doc/reference_manual:maps">Reference
+ Manual</seealso>.</p>
+ <p>
+ The current implementation is without the following
+ features: <taglist> <item>No variable keys</item>
+ <item>No single value access</item> <item>No map
+ comprehensions</item> </taglist></p>
+ <p>
+ Note that Maps is <em>experimental</em> during OTP 17.0.</p>
+ <p>
+ Own Id: OTP-11616</p>
+ </item>
+ <item>
+ <p>
+ Some function specs are corrected or moved and some edoc
+ comments are corrected in order to allow use of edoc.
+ (Thanks to Pierre Fenoll)</p>
+ <p>
+ Own Id: OTP-11702</p>
+ </item>
+ <item>
+ <p>
+ Thanks to Anthony Ramine for several improvements to the
+ optimizations in the BEAM compiler and for cleaning up
+ the code the code that transforms list and binary
+ comprehensions to Core Erlang.</p>
+ <p>
+ Own Id: OTP-11720</p>
+ </item>
+ <item>
+ <p>
+ The default encoding for Erlang source files is now
+ UTF-8. As a temporary measure to ease the transition from
+ the old default of latin-1, if the compiler encounters
+ byte sequences that are not valid UTF-8 sequences, the
+ compiler will re-try the compilation in latin-1 mode.
+ This workaround will be removed in a future release.</p>
+ <p>
+ Own Id: OTP-11791</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 4.9.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/src/beam_bool.erl b/lib/compiler/src/beam_bool.erl
index 124abd13c1..5a4621dc37 100644
--- a/lib/compiler/src/beam_bool.erl
+++ b/lib/compiler/src/beam_bool.erl
@@ -318,6 +318,8 @@ split_block_label_used([{set,[_],_,{bif,_,{f,Fail}}}|_], Fail) ->
true;
split_block_label_used([{set,[_],_,{alloc,_,{gc_bif,_,{f,Fail}}}}|_], Fail) ->
true;
+split_block_label_used([{set,[_],_,{alloc,_,{put_map,_,{f,Fail}}}}|_], Fail) ->
+ true;
split_block_label_used([_|Is], Fail) ->
split_block_label_used(Is, Fail);
split_block_label_used([], _) -> false.
@@ -391,10 +393,14 @@ bopt_tree([{set,_,_,{bif,'xor',_}}|_], _, _) ->
throw('xor');
bopt_tree([{protected,[Dst],Code,_}|Is], Forest0, Pre) ->
ProtForest0 = gb_trees:from_orddict([P || {_,any}=P <- gb_trees:to_list(Forest0)]),
- {ProtPre,[{_,ProtTree}]} = bopt_tree(Code, ProtForest0, []),
- Prot = {prot,ProtPre,ProtTree},
- Forest = gb_trees:enter(Dst, Prot, Forest0),
- bopt_tree(Is, Forest, Pre);
+ case bopt_tree(Code, ProtForest0, []) of
+ {ProtPre,[{_,ProtTree}]} ->
+ Prot = {prot,ProtPre,ProtTree},
+ Forest = gb_trees:enter(Dst, Prot, Forest0),
+ bopt_tree(Is, Forest, Pre);
+ _Res ->
+ throw(not_boolean_expr)
+ end;
bopt_tree([{set,[Dst],[Src],move}=Move|Is], Forest, Pre) ->
case {Src,Dst} of
{{tmp,_},_} -> throw(move);
@@ -432,9 +438,10 @@ bopt_bool_args(As, Forest) ->
mapfoldl(fun bopt_bool_arg/2, Forest, As).
bopt_bool_arg({T,_}=R, Forest) when T =:= x; T =:= y; T =:= tmp ->
- Val = case gb_trees:get(R, Forest) of
- any -> {test,is_eq_exact,fail,[R,{atom,true}]};
- Val0 -> Val0
+ Val = case gb_trees:lookup(R, Forest) of
+ {value,any} -> {test,is_eq_exact,fail,[R,{atom,true}]};
+ {value,Val0} -> Val0;
+ none -> throw(mixed)
end,
{Val,gb_trees:delete(R, Forest)};
bopt_bool_arg(Term, Forest) ->
@@ -525,7 +532,9 @@ bopt_cg({prot,Pre0,Tree}, Fail, Rs0, Acc, St0) ->
bopt_cg({atom,true}, _Fail, _Rs, Acc, St) ->
{Acc,St};
bopt_cg({atom,false}, Fail, _Rs, Acc, St) ->
- {[{jump,{f,Fail}}|Acc],St}.
+ {[{jump,{f,Fail}}|Acc],St};
+bopt_cg(_, _, _, _, _) ->
+ throw(not_boolean_expr).
bopt_cg_not({'and',As0}) ->
As = [bopt_cg_not(A) || A <- As0],
@@ -538,7 +547,9 @@ bopt_cg_not({'not',Arg}) ->
bopt_cg_not({test,Test,Fail,As}) ->
{inverted_test,Test,Fail,As};
bopt_cg_not({atom,Bool}) when is_boolean(Bool) ->
- {atom,not Bool}.
+ {atom,not Bool};
+bopt_cg_not(_) ->
+ throw(not_boolean_expr).
bopt_cg_not_not({'and',As}) ->
{'and',[bopt_cg_not_not(A) || A <- As]};
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index 4bdfe4e0c2..c45596f236 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -1030,6 +1030,7 @@ resolve_inst({gc_bif2,Args},Imports,_,_) ->
[F,Live,Bif,A1,A2,Reg] = resolve_args(Args),
{extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
{gc_bif,BifName,F,Live,[A1,A2],Reg};
+
%%
%% New instruction in R14, gc_bif with 3 arguments
%%
@@ -1146,21 +1147,17 @@ resolve_inst({put_map_assoc,Args},_,_,_) ->
[FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args,
List = resolve_args(List0),
{put_map_assoc,FLbl,Src,Dst,N,{list,List}};
-
resolve_inst({put_map_exact,Args},_,_,_) ->
[FLbl,Src,Dst,{u,N},{{z,1},{u,_Len},List0}] = Args,
List = resolve_args(List0),
{put_map_exact,FLbl,Src,Dst,N,{list,List}};
-
-resolve_inst({is_map,Args0},_,_,_) ->
+resolve_inst({is_map=I,Args0},_,_,_) ->
[FLbl|Args] = resolve_args(Args0),
- {test, is_map, FLbl, Args};
-
+ {test,I,FLbl,Args};
resolve_inst({has_map_fields,Args0},_,_,_) ->
[FLbl,Src,{{z,1},{u,_Len},List0}] = Args0,
List = resolve_args(List0),
{test,has_map_fields,FLbl,Src,{list,List}};
-
resolve_inst({get_map_elements,Args0},_,_,_) ->
[FLbl,Src,{{z,1},{u,_Len},List0}] = Args0,
List = resolve_args(List0),
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 0fc8d45c80..b952139f2c 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -446,11 +446,13 @@ is_label_used_in_2({set,_,_,Info}, Lbl) ->
case Info of
{bif,_,{f,F}} -> F =:= Lbl;
{alloc,_,{gc_bif,_,{f,F}}} -> F =:= Lbl;
+ {alloc,_,{put_map,_,{f,F}}} -> F =:= Lbl;
{'catch',{f,F}} -> F =:= Lbl;
{alloc,_,_} -> false;
{put_tuple,_} -> false;
{get_tuple_element,_} -> false;
{set_tuple_element,_} -> false;
+ {get_map_elements,{f,F}} -> F =:= Lbl;
{line,_} -> false;
_ when is_atom(Info) -> false
end.
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index 27034aecce..8ca368c167 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -748,6 +748,8 @@ live_opt([{try_end,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{loop_rec_end,_}=I|Is], Regs, D, Acc) ->
live_opt(Is, Regs, D, [I|Acc]);
+live_opt([{wait_timeout,_,nil}=I|Is], Regs, D, Acc) ->
+ live_opt(Is, Regs, D, [I|Acc]);
live_opt([{wait_timeout,_,{Tag,_}}=I|Is], Regs, D, Acc) when Tag =/= x ->
live_opt(Is, Regs, D, [I|Acc]);
live_opt([{line,_}=I|Is], Regs, D, Acc) ->
diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl
index 3c121f3b04..9d6768b157 100644
--- a/lib/compiler/src/cerl.erl
+++ b/lib/compiler/src/cerl.erl
@@ -124,21 +124,19 @@
%% keep map exports here for now
map_es/1,
- map_val/1,
+ map_arg/1,
update_c_map/3,
- ann_c_map/3,
+ c_map/1, is_c_map_empty/1,
+ ann_c_map/2, ann_c_map/3,
map_pair_op/1,map_pair_key/1,map_pair_val/1,
update_c_map_pair/4,
+ c_map_pair/2,
ann_c_map_pair/4
]).
--export_type([c_binary/0, c_call/0, c_clause/0, c_cons/0, c_fun/0, c_literal/0,
- c_module/0, c_tuple/0, c_values/0, c_var/0, cerl/0, var_name/0]).
-
-%%
-%% needed by the include file below -- do not move
-%%
--type var_name() :: integer() | atom() | {atom(), integer()}.
+-export_type([c_binary/0, c_bitstr/0, c_call/0, c_clause/0, c_cons/0, c_fun/0,
+ c_literal/0, c_map/0, c_map_pair/0, c_module/0, c_tuple/0,
+ c_values/0, c_var/0, cerl/0, var_name/0]).
-include("core_parse.hrl").
@@ -173,6 +171,8 @@
| c_module() | c_primop() | c_receive() | c_seq()
| c_try() | c_tuple() | c_values() | c_var().
+-type var_name() :: integer() | atom() | {atom(), integer()}.
+
%% =====================================================================
%% Representation (general)
%%
@@ -204,13 +204,15 @@
%% <td>call</td>
%% <td>case</td>
%% <td>catch</td>
-%% </tr><tr>
%% <td>clause</td>
+%% </tr><tr>
%% <td>cons</td>
%% <td>fun</td>
%% <td>let</td>
%% <td>letrec</td>
%% <td>literal</td>
+%% <td>map</td>
+%% <td>map_pair</td>
%% <td>module</td>
%% </tr><tr>
%% <td>primop</td>
@@ -261,10 +263,10 @@
%% @see subtrees/1
%% @see meta/1
--type ctype() :: 'alias' | 'apply' | 'binary' | 'bitrst' | 'call' | 'case'
- | 'catch' | 'clause' | 'cons' | 'fun' | 'let' | 'letrec'
- | 'literal' | 'map' | 'module' | 'primop' | 'receive' | 'seq'
- | 'try' | 'tuple' | 'values' | 'var'.
+-type ctype() :: 'alias' | 'apply' | 'binary' | 'bitrst' | 'call' | 'case'
+ | 'catch' | 'clause' | 'cons' | 'fun' | 'let' | 'letrec'
+ | 'literal' | 'map' | 'map_pair' | 'module' | 'primop'
+ | 'receive' | 'seq' | 'try' | 'tuple' | 'values' | 'var'.
-spec type(cerl()) -> ctype().
@@ -1575,25 +1577,91 @@ ann_make_list(_, [], Node) ->
%% ---------------------------------------------------------------------
%% maps
--spec map_es(c_map()) -> [cerl()].
+-spec map_es(c_map()) -> [c_map_pair()].
map_es(#c_map{es = Es}) ->
Es.
--spec map_val(c_map()) -> cerl().
-map_val(#c_map{var = M}) ->
+-spec map_arg(c_map()) -> c_map() | c_literal().
+
+map_arg(#c_map{arg=M}) ->
M.
+-spec c_map([c_map_pair()]) -> c_map().
+
+c_map(Pairs) ->
+ #c_map{es=Pairs}.
+
+-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 ann_c_map([term()], [cerl()]) -> c_map() | c_literal().
+
+ann_c_map(As,Es) ->
+ ann_c_map(As, #c_literal{val=#{}}, Es).
+
+-spec ann_c_map([term()], c_map() | c_literal(), [c_map_pair()]) -> c_map() | c_literal().
+
+ann_c_map(As,#c_literal{val=Mval}=M,Es) when is_map(Mval), map_size(Mval) =:= 0 ->
+ Pairs = [[Ck,Cv]||#c_map_pair{key=Ck,val=Cv}<-Es],
+ IsLit = lists:foldl(fun(Pair,Res) ->
+ Res andalso is_lit_list(Pair)
+ end, true, Pairs),
+ Fun = fun(Pair) -> [K,V] = lit_list_vals(Pair), {K,V} end,
+ case IsLit of
+ false ->
+ #c_map{arg=M, es=Es, anno=As };
+ true ->
+ #c_literal{anno=As, val=maps:from_list(lists:map(Fun, Pairs))}
+ end;
+ann_c_map(As,#c_literal{val=M},Es) when is_map(M) ->
+ fold_map_pairs(As,Es,M);
ann_c_map(As,M,Es) ->
- #c_map{var=M,es = Es, anno = As }.
+ #c_map{arg=M, es=Es, anno=As }.
+
+fold_map_pairs(As,[],M) -> #c_literal{anno=As,val=M};
+%% M#{ K => V}
+fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=assoc},key=Ck,val=Cv}=E|Es],M) ->
+ case is_lit_list([Ck,Cv]) of
+ true ->
+ [K,V] = lit_list_vals([Ck,Cv]),
+ fold_map_pairs(As,Es,maps:put(K,V,M));
+ false ->
+ #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As }
+ end;
+%% M#{ K := V}
+fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=exact},key=Ck,val=Cv}=E|Es],M) ->
+ case is_lit_list([Ck,Cv]) of
+ true ->
+ [K,V] = lit_list_vals([Ck,Cv]),
+ case maps:is_key(K,M) of
+ true -> fold_map_pairs(As,Es,maps:put(K,V,M));
+ false ->
+ #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As }
+ end;
+ false ->
+ #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As }
+ end;
+fold_map_pairs(As,Es,M) ->
+ #c_map{arg=#c_literal{val=M,anno=As}, es=Es, anno=As }.
+
+%-spec update_c_map(c_map() | c_literal(), [c_map_pair()]) -> c_map() | c_literal().
update_c_map(Old,M,Es) ->
- #c_map{var=M, es = Es, anno = get_ann(Old)}.
+ #c_map{arg=M, es = Es, anno = get_ann(Old)}.
map_pair_key(#c_map_pair{key=K}) -> K.
map_pair_val(#c_map_pair{val=V}) -> V.
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 ann_c_map_pair([term()], cerl(), cerl(), cerl()) ->
c_map_pair().
@@ -4195,6 +4263,9 @@ 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);
@@ -4324,12 +4395,8 @@ meta_1(cons, Node) ->
%% we get exactly one element, we generate a 'c_cons' call
%% instead of 'make_list' to reconstruct the node.
case split_list(Node) of
- {[H], none} ->
- meta_call(c_cons, [meta(H), meta(c_nil())]);
{[H], Node1} ->
meta_call(c_cons, [meta(H), meta(Node1)]);
- {L, none} ->
- meta_call(make_list, [make_list(meta_list(L))]);
{L, Node1} ->
meta_call(make_list,
[make_list(meta_list(L)), meta(Node1)])
@@ -4416,8 +4483,6 @@ split_list(Node, L) ->
case type(Node) of
cons when A =:= [] ->
split_list(cons_tl(Node), [cons_hd(Node) | L]);
- nil when A =:= [] ->
- {lists:reverse(L), none};
_ ->
{lists:reverse(L), Node}
end.
diff --git a/lib/compiler/src/cerl_clauses.erl b/lib/compiler/src/cerl_clauses.erl
index 99fa8dd9d5..87bd47c08b 100644
--- a/lib/compiler/src/cerl_clauses.erl
+++ b/lib/compiler/src/cerl_clauses.erl
@@ -354,6 +354,29 @@ match(P, E, Bs) ->
{false, Bs}
end
end;
+ map ->
+ %% The most we can do is to say "definitely no match" if a
+ %% map pattern is matched against non-map data.
+ case E of
+ any ->
+ {false, Bs};
+ _ ->
+ case type(E) of
+ literal ->
+ case is_map(concrete(E)) of
+ false ->
+ none;
+ true ->
+ {false, Bs}
+ end;
+ cons ->
+ none;
+ tuple ->
+ none;
+ _ ->
+ {false, Bs}
+ end
+ end;
_ ->
match_1(P, E, Bs)
end.
diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl
index 44293bb8ce..75740e8b9d 100644
--- a/lib/compiler/src/cerl_inline.erl
+++ b/lib/compiler/src/cerl_inline.erl
@@ -42,7 +42,7 @@
bitstr_flags/1, binary_segments/1, update_c_alias/3,
update_c_apply/3, update_c_binary/2, update_c_bitstr/6,
update_c_call/4, update_c_case/3, update_c_catch/2,
- update_c_clause/4, c_fun/2, c_int/1, c_let/3,
+ update_c_clause/4, c_fun/2, c_int/1, c_let/3, ann_c_let/4,
update_c_let/4, update_c_letrec/3, update_c_module/5,
update_c_primop/3, update_c_receive/4, update_c_seq/3,
c_seq/2, update_c_try/6, c_tuple/1, update_c_values/2,
@@ -51,7 +51,7 @@
catch_body/1, clause_body/1, clause_guard/1,
clause_pats/1, clause_vars/1, concrete/1, cons_hd/1,
cons_tl/1, data_arity/1, data_es/1, data_type/1,
- fun_body/1, fun_vars/1, get_ann/1, int_val/1,
+ fname_arity/1, fun_body/1, fun_vars/1, get_ann/1, int_val/1,
is_c_atom/1, is_c_cons/1, is_c_fname/1, is_c_int/1,
is_c_list/1, is_c_seq/1, is_c_tuple/1, is_c_var/1,
is_data/1, is_literal/1, is_literal_term/1, let_arg/1,
@@ -64,7 +64,7 @@
seq_body/1, set_ann/2, try_arg/1, try_body/1, try_vars/1,
try_evars/1, try_handler/1, tuple_es/1, tuple_arity/1,
type/1, values_es/1, var_name/1,
- map_val/1, map_es/1, update_c_map/3,
+ map_arg/1, map_es/1, update_c_map/3,
update_c_map_pair/4,
map_pair_op/1, map_pair_key/1, map_pair_val/1
]).
@@ -1030,8 +1030,17 @@ i_apply(E, Ctxt, Ren, Env, S) ->
visit_and_count_size(Opnd, S)
end,
S3, Opnds),
- N = apply_size(length(Es)),
- {update_c_apply(E, E1, Es), count_size(N, S4)}
+ Arity = length(Es),
+ E2 = case is_c_fname(E1) andalso length(Es) =/= fname_arity(E1) of
+ true ->
+ V = new_var(Env),
+ ann_c_let(get_ann(E), [V], E1,
+ update_c_apply(E, V, Es));
+ false ->
+ update_c_apply(E, E1, Es)
+ end,
+ N = apply_size(Arity),
+ {E2, count_size(N, S4)}
end.
apply_size(A) ->
@@ -1334,7 +1343,7 @@ i_bitstr(E, Ren, Env, S) ->
i_map(E, Ctx, Ren, Env, S) ->
%% Visit the segments for value.
- {M1, S1} = i(map_val(E), value, Ren, Env, S),
+ {M1, S1} = i(map_arg(E), value, Ren, Env, S),
{Es, S2} = mapfoldl(fun (E, S) ->
i_map_pair(E, Ctx, Ren, Env, S)
end, S1, map_es(E)),
@@ -1411,8 +1420,8 @@ i_pattern(E, Ren, Env, Ren0, Env0, S) ->
S2 = count_size(weight(binary), S1),
{update_c_binary(E, Es), S2};
map ->
- %% map patterns should not have vals
- M = map_val(E),
+ %% map patterns should not have args
+ M = map_arg(E),
{Es, S1} = mapfoldl(fun (E, S) ->
i_map_pair_pattern(E, Ren, Env, Ren0, Env0, S)
diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl
index 2ebeab243f..e53bdd4efb 100644
--- a/lib/compiler/src/cerl_trees.erl
+++ b/lib/compiler/src/cerl_trees.erl
@@ -57,7 +57,7 @@
update_c_try/6, update_c_tuple/2, update_c_tuple_skel/2,
update_c_values/2, values_es/1, var_name/1,
- map_val/1, map_es/1,
+ map_arg/1, map_es/1,
ann_c_map/3,
update_c_map/3,
map_pair_key/1,map_pair_val/1,map_pair_op/1,
@@ -138,7 +138,7 @@ map_1(F, T) ->
tuple ->
update_c_tuple_skel(T, map_list(F, tuple_es(T)));
map ->
- update_c_map(T, map(F,map_val(T)), map_list(F, map_es(T)));
+ update_c_map(T, map(F, map_arg(T)), map_list(F, map_es(T)));
map_pair ->
update_c_map_pair(T, map(F, map_pair_op(T)),
map(F, map_pair_key(T)),
@@ -372,7 +372,7 @@ mapfold(F, S0, T) ->
{Ts, S1} = mapfold_list(F, S0, tuple_es(T)),
F(update_c_tuple_skel(T, Ts), S1);
map ->
- {M , S1} = mapfold(F, S0, map_val(T)),
+ {M , S1} = mapfold(F, S0, map_arg(T)),
{Ts, S2} = mapfold_list(F, S1, map_es(T)),
F(update_c_map(T, M, Ts), S2);
map_pair ->
@@ -724,7 +724,7 @@ label(T, N, Env) ->
{As, N2} = label_ann(T, N1),
{ann_c_tuple_skel(As, Ts), N2};
map ->
- {M, N1} = label(map_val(T), N, Env),
+ {M, N1} = label(map_arg(T), N, Env),
{Ts, N2} = label_list(map_es(T), N1, Env),
{As, N3} = label_ann(T, N2),
{ann_c_map(As, M, Ts), N3};
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index b88f9792a5..c7d91070f6 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -234,7 +234,9 @@ format_error({crash,Pass,Reason}) ->
format_error({bad_return,Pass,Reason}) ->
io_lib:format("internal error in ~p;\nbad return value: ~ts", [Pass,format_error_reason(Reason)]);
format_error({module_name,Mod,Filename}) ->
- io_lib:format("Module name '~s' does not match file name '~ts'", [Mod,Filename]).
+ io_lib:format("Module name '~s' does not match file name '~ts'", [Mod,Filename]);
+format_error(reparsing_invalid_unicode) ->
+ "Non-UTF-8 character(s) detected, but no encoding declared. Encode the file in UTF-8 or add \"%% coding: latin-1\" at the beginning of the file. Retrying with latin-1 encoding.".
format_error_reason({Reason, Stack}) when is_list(Stack) ->
StackFun = fun
@@ -246,7 +248,7 @@ format_error_reason({Reason, Stack}) when is_list(Stack) ->
end,
FormatFun = fun (Term, _) -> io_lib:format("~tp", [Term]) end,
[io_lib:format("~tp", [Reason]),"\n\n",
- lib:format_stacktrace(1, erlang:get_stacktrace(), StackFun, FormatFun)];
+ lib:format_stacktrace(1, Stack, StackFun, FormatFun)];
format_error_reason(Reason) ->
io_lib:format("~tp", [Reason]).
@@ -792,20 +794,59 @@ no_native_compilation(BeamFile, #compile{options=Opts0}) ->
_ -> false
end.
-parse_module(St) ->
- Opts = St#compile.options,
- Cwd = ".",
- IncludePath = [Cwd, St#compile.dir|inc_paths(Opts)],
- R = epp:parse_file(St#compile.ifile, IncludePath, pre_defs(Opts)),
+parse_module(St0) ->
+ case do_parse_module(utf8, St0) of
+ {ok,_}=Ret ->
+ Ret;
+ {error,_}=Ret ->
+ Ret;
+ {invalid_unicode,File,Line} ->
+ case do_parse_module(latin1, St0) of
+ {ok,St} ->
+ Es = [{File,[{Line,?MODULE,reparsing_invalid_unicode}]}],
+ {ok,St#compile{warnings=Es++St#compile.warnings}};
+ {error,St} ->
+ Es = [{File,[{Line,?MODULE,reparsing_invalid_unicode}]}],
+ {error,St#compile{errors=Es++St#compile.errors}}
+ end
+ end.
+
+do_parse_module(DefEncoding, #compile{ifile=File,options=Opts,dir=Dir}=St) ->
+ R = epp:parse_file(File,
+ [{includes,[".",Dir|inc_paths(Opts)]},
+ {macros,pre_defs(Opts)},
+ {default_encoding,DefEncoding},
+ extra]),
case R of
- {ok,Forms} ->
- Encoding = epp:read_encoding(St#compile.ifile),
- {ok,St#compile{code=Forms,encoding=Encoding}};
+ {ok,Forms,Extra} ->
+ Encoding = proplists:get_value(encoding, Extra),
+ case find_invalid_unicode(Forms, File) of
+ none ->
+ {ok,St#compile{code=Forms,encoding=Encoding}};
+ {invalid_unicode,_,_}=Ret ->
+ case Encoding of
+ none ->
+ Ret;
+ _ ->
+ {ok,St#compile{code=Forms,encoding=Encoding}}
+ end
+ end;
{error,E} ->
Es = [{St#compile.ifile,[{none,?MODULE,{epp,E}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}}
end.
+find_invalid_unicode([H|T], File0) ->
+ case H of
+ {attribute,_,file,{File,_}} ->
+ find_invalid_unicode(T, File);
+ {error,{Line,file_io_server,invalid_unicode}} ->
+ {invalid_unicode,File0,Line};
+ _Other ->
+ find_invalid_unicode(T, File0)
+ end;
+find_invalid_unicode([], _) -> none.
+
parse_core(St) ->
case file:read_file(St#compile.ifile) of
{ok,Bin} ->
diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src
index 8775c84698..8f68915f8e 100644
--- a/lib/compiler/src/compiler.app.src
+++ b/lib/compiler/src/compiler.app.src
@@ -67,4 +67,6 @@
]},
{registered, []},
{applications, [kernel, stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","hipe-3.10.3","erts-6.0",
+ "crypto-3.3"]}]}.
diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl
index ed181e3baa..2792fd8fa5 100644
--- a/lib/compiler/src/core_lib.erl
+++ b/lib/compiler/src/core_lib.erl
@@ -59,7 +59,7 @@ is_lit_bin(Es) ->
%% Return the value of LitExpr.
-spec literal_value(cerl:c_literal() | cerl:c_binary() |
- cerl:c_cons() | cerl:c_tuple()) -> term().
+ cerl:c_map() | cerl:c_cons() | cerl:c_tuple()) -> term().
literal_value(#c_literal{val=V}) -> V;
literal_value(#c_binary{segments=Es}) ->
@@ -67,7 +67,14 @@ literal_value(#c_binary{segments=Es}) ->
literal_value(#c_cons{hd=H,tl=T}) ->
[literal_value(H)|literal_value(T)];
literal_value(#c_tuple{es=Es}) ->
- list_to_tuple(literal_value_list(Es)).
+ list_to_tuple(literal_value_list(Es));
+literal_value(#c_map{arg=Cm,es=Cmps}) ->
+ M = literal_value(Cm),
+ lists:foldl(fun(#c_map_pair{ key=Ck, val=Cv },Mi) ->
+ K = literal_value(Ck),
+ V = literal_value(Cv),
+ maps:put(K,V,Mi)
+ end, M, Cmps).
literal_value_list(Vals) -> [literal_value(V) || V <- Vals].
@@ -105,7 +112,7 @@ vu_expr(V, #c_cons{hd=H,tl=T}) ->
vu_expr(V, H) orelse vu_expr(V, T);
vu_expr(V, #c_tuple{es=Es}) ->
vu_expr_list(V, Es);
-vu_expr(V, #c_map{var=M,es=Es}) ->
+vu_expr(V, #c_map{arg=M,es=Es}) ->
vu_expr(V, M) orelse vu_expr_list(V, Es);
vu_expr(V, #c_map_pair{key=Key,val=Val}) ->
vu_expr_list(V, [Key,Val]);
diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl
index 36165245a6..25df33a287 100644
--- a/lib/compiler/src/core_lint.erl
+++ b/lib/compiler/src/core_lint.erl
@@ -267,10 +267,21 @@ gexpr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) ->
St1 = gbody(Arg, Def, let_varcount(Vs), St0), %This is a guard body
{Lvs,St2} = variable_list(Vs, St1),
gbody(B, union(Lvs, Def), Rt, St2);
-gexpr(#c_call{module=#c_literal{val=erlang},
- name=#c_literal{},
- args=As}, Def, 1, St) ->
- gexpr_list(As, Def, St);
+gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=is_record},
+ args=[Arg,#c_literal{val=Tag},#c_literal{val=Size}]},
+ Def, 1, St) when is_atom(Tag), is_integer(Size) ->
+ gexpr(Arg, Def, 1, St);
+gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=is_record}},
+ _Def, 1, St) ->
+ add_error({illegal_guard,St#lint.func}, St);
+gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=Name},args=As},
+ Def, 1, St) when is_atom(Name) ->
+ case is_guard_bif(Name, length(As)) of
+ true ->
+ gexpr_list(As, Def, St);
+ false ->
+ add_error({illegal_guard,St#lint.func}, St)
+ end;
gexpr(#c_primop{name=#c_literal{val=A},args=As}, Def, _Rt, St0) when is_atom(A) ->
gexpr_list(As, Def, St0);
gexpr(#c_try{arg=E,vars=[#c_var{name=X}],body=#c_var{name=X},
@@ -298,6 +309,14 @@ gbitstr_list(Es, Def, St0) ->
gbitstr(#c_bitstr{val=V,size=S}, Def, St) ->
gexpr_list([V,S], Def, St).
+%% is_guard_bif(Name, Arity) -> Boolean.
+
+is_guard_bif(Name, Arity) ->
+ erl_internal:guard_bif(Name, Arity)
+ orelse erl_internal:arith_op(Name, Arity)
+ orelse erl_internal:bool_op(Name, Arity)
+ orelse erl_internal:comp_op(Name, Arity).
+
%% expr(Expr, Defined, RetCount, State) -> State.
expr(#c_var{name={_,_}=FA}, Def, _Rt, St) ->
diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl
index d54715ef59..4a00535360 100644
--- a/lib/compiler/src/core_parse.hrl
+++ b/lib/compiler/src/core_parse.hrl
@@ -34,7 +34,7 @@
-record(c_apply, {anno=[], op, % op :: Tree,
args}). % args :: [Tree]
--record(c_binary, {anno=[], segments}). % segments :: [#c_bitstr{}]
+-record(c_binary, {anno=[], segments :: [cerl:c_bitstr()]}).
-record(c_bitstr, {anno=[], val, % val :: Tree,
size, % size :: Tree,
@@ -70,6 +70,15 @@
-record(c_literal, {anno=[], val}). % val :: literal()
+-record(c_map, {anno=[],
+ arg=#c_literal{val=#{}} :: cerl:c_var() | cerl:c_literal(),
+ es :: [cerl:c_map_pair()]}).
+
+-record(c_map_pair, {anno=[],
+ op :: #c_literal{val::'assoc'} | #c_literal{val::'exact'},
+ key,
+ val}).
+
-record(c_module, {anno=[], name, % name :: Tree,
exports, % exports :: [Tree],
attrs, % attrs :: [#c_def{}],
@@ -96,12 +105,3 @@
-record(c_values, {anno=[], es}). % es :: [Tree]
-record(c_var, {anno=[], name :: cerl:var_name()}).
-
--record(c_map_pair, {anno=[],
- op :: #c_literal{val::'assoc'} | #c_literal{val::'exact'},
- key,
- val}).
-
--record(c_map, {anno=[],
- var=#c_literal{val=[]} :: #c_var{} | #c_literal{},
- es :: [#c_map_pair{}]}).
diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl
index b8db0f683a..a66ad4235f 100644
--- a/lib/compiler/src/core_parse.yrl
+++ b/lib/compiler/src/core_parse.yrl
@@ -21,7 +21,7 @@
%% Have explicit productions for annotated phrases named anno_XXX.
%% This just does an XXX and adds the annotation.
-Expect 1.
+Expect 0.
Nonterminals
@@ -285,9 +285,9 @@ tuple -> '{' '}' : c_tuple([]).
tuple -> '{' anno_expressions '}' : c_tuple('$2').
map_expr -> '~' '{' '}' '~' : #c_map{es=[]}.
-map_expr -> '~' '{' map_pairs '}' '~' : #c_map{es='$3'}.
-map_expr -> variable '~' '{' '}' '~' : #c_map{var='$1',es=[]}.
-map_expr -> variable '~' '{' map_pairs '}' '~' : #c_map{var='$1',es='$4'}.
+map_expr -> '~' '{' map_pairs '}' '~' : #c_map{es='$3'}.
+map_expr -> '~' '{' map_pairs '|' variable '}' '~' : #c_map{arg='$5',es='$3'}.
+map_expr -> '~' '{' map_pairs '|' map_expr '}' '~' : #c_map{arg='$5',es='$3'}.
map_pairs -> map_pair : ['$1'].
map_pairs -> map_pair ',' map_pairs : ['$1' | '$3'].
diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl
index faa26ec6df..83412ecdd7 100644
--- a/lib/compiler/src/core_pp.erl
+++ b/lib/compiler/src/core_pp.erl
@@ -118,6 +118,16 @@ format_1(#c_literal{val=Tuple}, Ctxt) when is_tuple(Tuple) ->
format_1(#c_literal{anno=A,val=Bitstring}, Ctxt) when is_bitstring(Bitstring) ->
Segs = segs_from_bitstring(Bitstring),
format_1(#c_binary{anno=A,segments=Segs}, Ctxt);
+format_1(#c_literal{anno=A,val=M},Ctxt) when is_map(M) ->
+ Pairs = maps:to_list(M),
+ Op = case Ctxt of
+ #ctxt{ class = clause } -> exact;
+ _ -> assoc
+ end,
+ Cpairs = [#c_map_pair{op=#c_literal{val=Op},
+ key=#c_literal{val=V},
+ val=#c_literal{val=K}} || {K,V} <- Pairs],
+ format_1(#c_map{anno=A,arg=#c_literal{val=#{}},es=Cpairs},Ctxt);
format_1(#c_var{name={I,A}}, _) ->
[core_atom(I),$/,integer_to_list(A)];
format_1(#c_var{name=V}, _) ->
@@ -161,15 +171,15 @@ format_1(#c_tuple{es=Es}, Ctxt) ->
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
$}
];
-format_1(#c_map{var=#c_var{}=Var,es=Es}, Ctxt) ->
- [format_1(Var, Ctxt),
- "~{",
+format_1(#c_map{arg=#c_literal{val=M},es=Es}, Ctxt) when is_map(M),map_size(M)=:=0 ->
+ ["~{",
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
"}~"
];
-format_1(#c_map{es=Es}, Ctxt) ->
+format_1(#c_map{arg=Var,es=Es}, Ctxt) ->
["~{",
format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
+ "|",format(Var, add_indent(Ctxt, 1)),
"}~"
];
format_1(#c_map_pair{op=#c_literal{val=assoc},key=K,val=V}, Ctxt) ->
diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl
index 3ad3c8c690..6c75538194 100644
--- a/lib/compiler/src/erl_bifs.erl
+++ b/lib/compiler/src/erl_bifs.erl
@@ -91,6 +91,7 @@ is_pure(erlang, is_float, 1) -> true;
is_pure(erlang, is_function, 1) -> true;
is_pure(erlang, is_integer, 1) -> true;
is_pure(erlang, is_list, 1) -> true;
+is_pure(erlang, is_map, 1) -> true;
is_pure(erlang, is_number, 1) -> true;
is_pure(erlang, is_pid, 1) -> true;
is_pure(erlang, is_port, 1) -> true;
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index eb9c302334..ce40213bad 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -72,7 +72,7 @@
-import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,all/2,any/2,
reverse/1,reverse/2,member/2,nth/2,flatten/1,unzip/1]).
--import(cerl, [ann_c_cons/3,ann_c_tuple/2]).
+-import(cerl, [ann_c_cons/3,ann_c_map/3,ann_c_tuple/2]).
-include("core_parse.hrl").
@@ -246,7 +246,7 @@ expr(#c_tuple{anno=Anno,es=Es0}=Tuple, Ctxt, Sub) ->
value ->
ann_c_tuple(Anno, Es)
end;
-expr(#c_map{var=V0,es=Es0}=Map, Ctxt, Sub) ->
+expr(#c_map{anno=Anno,arg=V0,es=Es0}=Map, Ctxt, Sub) ->
Es = pair_list(Es0, Ctxt, Sub),
case Ctxt of
effect ->
@@ -254,7 +254,7 @@ expr(#c_map{var=V0,es=Es0}=Map, Ctxt, Sub) ->
expr(make_effect_seq(Es, Sub), Ctxt, Sub);
value ->
V = expr(V0, Ctxt, Sub),
- Map#c_map{var=V,es=Es}
+ ann_c_map(Anno,V,Es)
end;
expr(#c_binary{segments=Ss}=Bin0, Ctxt, Sub) ->
%% Warn for useless building, but always build the binary
@@ -1378,6 +1378,7 @@ eval_is_record(Call, _, _, _, _) -> Call.
is_not_integer(#c_literal{val=Val}) when not is_integer(Val) -> true;
is_not_integer(#c_tuple{}) -> true;
is_not_integer(#c_cons{}) -> true;
+is_not_integer(#c_map{}) -> true;
is_not_integer(_) -> false.
%% is_not_tuple(Core) -> true | false.
@@ -1385,6 +1386,7 @@ is_not_integer(_) -> false.
is_not_tuple(#c_literal{val=Val}) when not is_tuple(Val) -> true;
is_not_tuple(#c_cons{}) -> true;
+is_not_tuple(#c_map{}) -> true;
is_not_tuple(_) -> false.
%% eval_setelement(Call, Pos, Tuple, NewVal) -> Core.
@@ -1554,16 +1556,8 @@ map_pair_pattern_list(Ps0, Isub, Osub0) ->
{Ps,Osub}.
map_pair_pattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair,{Isub,Osub0}) ->
- {K,Osub1} = case cerl:type(K0) of
- binary ->
- K1 = eval_binary(K0),
- case cerl:type(K1) of
- literal -> {K1,Osub0};
- _ -> pattern(K0,Isub,Osub0)
- end;
- _ -> pattern(K0,Isub,Osub0)
- end,
- {V,Osub} = pattern(V0,Isub,Osub1),
+ K = expr(K0, Isub),
+ {V,Osub} = pattern(V0,Isub,Osub0),
{Pair#c_map_pair{key=K,val=V},{Isub,Osub}}.
bin_pattern_list(Ps0, Isub, Osub0) ->
@@ -1810,9 +1804,14 @@ opt_bool_clauses([#c_clause{pats=[#c_literal{val=Lit}],
true ->
%% This clause will match.
C = C0#c_clause{body=opt_bool_case(B)},
- case Lit of
- false -> [C|opt_bool_clauses(Cs, SeenT, true)];
- true -> [C|opt_bool_clauses(Cs, true, SeenF)]
+ case {Lit,SeenT,SeenF} of
+ {false,_,false} ->
+ [C|opt_bool_clauses(Cs, SeenT, true)];
+ {true,false,_} ->
+ [C|opt_bool_clauses(Cs, true, SeenF)];
+ _ ->
+ add_warning(C, nomatch_shadow),
+ opt_bool_clauses(Cs, SeenT, SeenF)
end
end;
opt_bool_clauses([#c_clause{pats=Ps,guard=#c_literal{val=true}}=C|Cs], SeenT, SeenF) ->
@@ -2024,9 +2023,9 @@ case_opt(Arg, Cs0, Sub) ->
case_opt_args([A0|As0], Cs0, Sub, LitExpr, Acc) ->
case case_opt_arg(A0, Sub, Cs0, LitExpr) of
- error ->
+ {error,Cs1} ->
%% Nothing to be done. Move on to the next argument.
- Cs = [{Ps,C,[P|PsAcc],Bs} || {[P|Ps],C,PsAcc,Bs} <- Cs0],
+ Cs = [{Ps,C,[P|PsAcc],Bs} || {[P|Ps],C,PsAcc,Bs} <- Cs1],
case_opt_args(As0, Cs, Sub, LitExpr, [A0|Acc]);
{ok,As1,Cs} ->
%% The argument was either expanded (from tuple/list) or
@@ -2045,7 +2044,7 @@ case_opt_arg(E0, Sub, Cs, LitExpr) ->
E = maybe_replace_var(E0, Sub),
case cerl:is_data(E) of
false ->
- error;
+ {error,Cs};
true ->
case cerl:data_type(E) of
{atomic,_} ->
@@ -2095,35 +2094,44 @@ maybe_replace_var_1(E, #sub{t=Tdb}) ->
%% pattern matching is tricky, so we will give up in that case.
case_opt_lit(Lit, Cs0, LitExpr) ->
- try case_opt_lit_1(Cs0, Lit, LitExpr) of
+ Cs1 = case_opt_lit_1(Lit, Cs0, LitExpr),
+ try case_opt_lit_2(Lit, Cs1) of
Cs ->
{ok,[],Cs}
catch
throw:impossible ->
- error
+ {error,Cs1}
end.
-case_opt_lit_1([{[P|Ps],C,PsAcc,Bs0}|Cs], E, LitExpr) ->
+case_opt_lit_1(E, [{[P|_],C,_,_}=Current|Cs], LitExpr) ->
+ case cerl_clauses:match(P, E) of
+ none ->
+ %% The pattern will not match the literal. Remove the clause.
+ %% Unless the entire case expression is a literal, also
+ %% emit a warning.
+ case LitExpr of
+ false -> add_warning(C, nomatch_clause_type);
+ true -> ok
+ end,
+ case_opt_lit_1(E, Cs, LitExpr);
+ _ ->
+ [Current|case_opt_lit_1(E, Cs, LitExpr)]
+ end;
+case_opt_lit_1(_, [], _) -> [].
+
+case_opt_lit_2(E, [{[P|Ps],C,PsAcc,Bs0}|Cs]) ->
+ %% Non-matching clauses have already been removed in case_opt_lit_1/3.
case cerl_clauses:match(P, E) of
- none ->
- %% The pattern will not match the literal. Remove the clause.
- %% Unless the entire case expression is a literal, also
- %% emit a warning.
- case LitExpr of
- false -> add_warning(C, nomatch_clause_type);
- true -> ok
- end,
- case_opt_lit_1(Cs, E, LitExpr);
{true,Bs} ->
%% The pattern matches the literal. Remove the pattern
%% and update the bindings.
- [{Ps,C,PsAcc,Bs++Bs0}|case_opt_lit_1(Cs, E, LitExpr)];
+ [{Ps,C,PsAcc,Bs++Bs0}|case_opt_lit_2(E, Cs)];
{false,_} ->
%% Binary literal and pattern. We are not sure whether
%% the pattern will match.
throw(impossible)
end;
-case_opt_lit_1([], _, _) -> [].
+case_opt_lit_2(_, []) -> [].
%% case_opt_data(Expr, Clauses0, LitExpr) -> {ok,Exprs,Clauses}
@@ -2346,16 +2354,31 @@ is_safe_bool_expr(Core, Sub) ->
is_safe_bool_expr_1(Core, Sub, gb_sets:empty()).
is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
- name=#c_literal{val=is_record},
- args=[_,_]},
- _Sub, _BoolVars) ->
+ name=#c_literal{val=is_record},
+ args=[A,#c_literal{val=Tag},#c_literal{val=Size}]},
+ Sub, _BoolVars) when is_atom(Tag), is_integer(Size) ->
+ is_safe_simple(A, Sub);
+is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_record}},
+ _Sub, _BoolVars) ->
%% The is_record/2 BIF is NOT allowed in guards.
+ %% The is_record/3 BIF where its second argument is not an atom or its third
+ %% is not an integer is NOT allowed in guards.
%%
%% NOTE: Calls like is_record(Expr, LiteralTag), where LiteralTag
%% is a literal atom referring to a defined record, have already
%% been rewritten to is_record(Expr, LiteralTag, TupleSize).
false;
is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function},
+ args=[A,#c_literal{val=Arity}]},
+ Sub, _BoolVars) when is_integer(Arity), Arity >= 0 ->
+ is_safe_simple(A, Sub);
+is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=is_function}},
+ _Sub, _BoolVars) ->
+ false;
+is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=Name},args=Args},
Sub, BoolVars) ->
NumArgs = length(Args),
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index 9998043013..761ae8409c 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -232,7 +232,7 @@ pattern({map,Line,Ps}, St0) ->
{TPs,St1} = pattern_list(Ps, St0),
{{map,Line,TPs},St1};
pattern({map_field_exact,Line,K0,V0}, St0) ->
- {K,St1} = pattern(K0, St0),
+ {K,St1} = expr(K0, St0),
{V,St2} = pattern(V0, St1),
{{map_field_exact,Line,K,V},St2};
%%pattern({struct,Line,Tag,Ps}, St0) ->
@@ -331,9 +331,10 @@ expr({tuple,Line,Es0}, St0) ->
expr({map,Line,Es0}, St0) ->
{Es1,St1} = expr_list(Es0, St0),
{{map,Line,Es1},St1};
-expr({map,Line,Var,Es0}, St0) ->
- {Es1,St1} = expr_list(Es0, St0),
- {{map,Line,Var,Es1},St1};
+expr({map,Line,E0,Es0}, St0) ->
+ {E1,St1} = expr(E0, St0),
+ {Es1,St2} = expr_list(Es0, St1),
+ {{map,Line,E1,Es1},St2};
expr({map_field_assoc,Line,K0,V0}, St0) ->
{K,St1} = expr(K0, St0),
{V,St2} = expr(V0, St1),
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index e00ee1f3ad..47a357c23d 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -960,7 +960,6 @@ select_extract_map(Src, Vs, Fail, I, Vdb, Bef, St) ->
end, {{[],[]},Bef}, Vs),
Code = case {HasKs,GetVs} of
- {[],[]} -> {[],Aft,St};
{HasKs,[]} ->
[{test,has_map_fields,{f,Fail},Rsrc,{list,HasKs}}];
{[],GetVs} ->
@@ -1551,7 +1550,10 @@ map_pair_strip_and_termsort(Es) ->
%% [{map_pair,K,V}]
%% where K is for example {integer, 1} and we want to sort on 1.
Ls = [{K,V}||{_,K,V}<-Es],
- lists:sort(fun({{_,A},_},{{_,B},_}) -> erts_internal:cmp_term(A,B) < 0 end, Ls).
+ lists:sort(fun ({{_,A},_}, {{_,B},_}) -> erts_internal:cmp_term(A,B) =< 0;
+ ({nil,_}, {{_,B},_}) -> [] =< B;
+ ({{_,A},_}, {nil,_}) -> A =< []
+ end, Ls).
%%%
%%% Code generation for constructing binaries.
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index a50b46bd7b..8c18f6a9f7 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -77,7 +77,8 @@
splitwith/2,keyfind/3,sort/1,foreach/2,droplast/1,last/1]).
-import(ordsets, [add_element/2,del_element/2,is_element/2,
union/1,union/2,intersection/2,subtract/2]).
--import(cerl, [ann_c_cons/3,ann_c_cons_skel/3,ann_c_tuple/2,c_tuple/1]).
+-import(cerl, [ann_c_cons/3,ann_c_cons_skel/3,ann_c_tuple/2,c_tuple/1,
+ ann_c_map/2, ann_c_map/3]).
-include("core_parse.hrl").
@@ -274,51 +275,67 @@ gexpr({op,L,'orelse',E1,E2}, Bools, St0) ->
True = {atom,L,true},
E = make_bool_switch_guard(L, E1, V, True, E2),
gexpr(E, Bools, St);
-gexpr({op,Line,Op,L,R}=Call, Bools0, St0) ->
+gexpr({op,Line,Op,L,R}=E, Bools, St) ->
case erl_internal:bool_op(Op, 2) of
- true ->
- {Le,Lps,Bools1,St1} = gexpr(L, Bools0, St0),
- {Ll,Llps,St2} = force_safe(Le, St1),
- {Re,Rps,Bools,St3} = gexpr(R, Bools1, St2),
- {Rl,Rlps,St4} = force_safe(Re, St3),
- Anno = lineno_anno(Line, St4),
- {#icall{anno=#a{anno=Anno}, %Must have an #a{}
- module=#c_literal{anno=Anno,val=erlang},
- name=#c_literal{anno=Anno,val=Op},
- args=[Ll,Rl]},Lps ++ Llps ++ Rps ++ Rlps,Bools,St4};
- false ->
- gexpr_test(Call, Bools0, St0)
+ true ->
+ gexpr_bool(Op, L, R, Bools, St, Line);
+ false ->
+ gexpr_test(E, Bools, St)
end;
-gexpr({op,Line,Op,A}=Call, Bools0, St0) ->
- case Op of
- 'not' ->
- {Ae0,Aps,Bools,St1} = gexpr(A, Bools0, St0),
- case Ae0 of
- #icall{module=#c_literal{val=erlang},
- name=#c_literal{val='=:='},
- args=[E,#c_literal{val=true}]}=EqCall ->
- %%
- %% Doing the following transformation
- %% not(Expr =:= true) ==> Expr =:= false
- %% will help eliminating redundant is_boolean/1 tests.
- %%
- Ae = EqCall#icall{args=[E,#c_literal{val=false}]},
- {Al,Alps,St2} = force_safe(Ae, St1),
- {Al,Aps ++ Alps,Bools,St2};
- Ae ->
- {Al,Alps,St2} = force_safe(Ae, St1),
- Anno = lineno_anno(Line, St2),
- {#icall{anno=#a{anno=Anno}, %Must have an #a{}
- module=#c_literal{anno=Anno,val=erlang},
- name=#c_literal{anno=Anno,val=Op},
- args=[Al]},Aps ++ Alps,Bools,St2}
- end;
- _ ->
- gexpr_test(Call, Bools0, St0)
+gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,Op}},[L,R]}=E, Bools, St) ->
+ case erl_internal:bool_op(Op, 2) of
+ true ->
+ gexpr_bool(Op, L, R, Bools, St, Line);
+ false ->
+ gexpr_test(E, Bools, St)
end;
+gexpr({op,Line,'not',A}, Bools, St) ->
+ gexpr_not(A, Bools, St, Line);
+gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,'not'}},[A]}, Bools, St) ->
+ gexpr_not(A, Bools, St, Line);
gexpr(E0, Bools, St0) ->
gexpr_test(E0, Bools, St0).
+%% gexpr_not(L, R, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate a guard for boolean operators
+
+gexpr_bool(Op, L, R, Bools0, St0, Line) ->
+ {Le,Lps,Bools1,St1} = gexpr(L, Bools0, St0),
+ {Ll,Llps,St2} = force_safe(Le, St1),
+ {Re,Rps,Bools,St3} = gexpr(R, Bools1, St2),
+ {Rl,Rlps,St4} = force_safe(Re, St3),
+ Anno = lineno_anno(Line, St4),
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_literal{anno=Anno,val=erlang},
+ name=#c_literal{anno=Anno,val=Op},
+ args=[Ll,Rl]},Lps ++ Llps ++ Rps ++ Rlps,Bools,St4}.
+
+%% gexpr_not(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate an erlang:'not'/1 guard test.
+
+gexpr_not(A, Bools0, St0, Line) ->
+ {Ae0,Aps,Bools,St1} = gexpr(A, Bools0, St0),
+ case Ae0 of
+ #icall{module=#c_literal{val=erlang},
+ name=#c_literal{val='=:='},
+ args=[E,#c_literal{val=true}]}=EqCall ->
+ %%
+ %% Doing the following transformation
+ %% not(Expr =:= true) ==> Expr =:= false
+ %% will help eliminating redundant is_boolean/1 tests.
+ %%
+ Ae = EqCall#icall{args=[E,#c_literal{val=false}]},
+ {Al,Alps,St2} = force_safe(Ae, St1),
+ {Al,Aps ++ Alps,Bools,St2};
+ Ae ->
+ {Al,Alps,St2} = force_safe(Ae, St1),
+ Anno = lineno_anno(Line, St2),
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_literal{anno=Anno,val=erlang},
+ name=#c_literal{anno=Anno,val='not'},
+ args=[Al]},Aps ++ Alps,Bools,St2}
+ end.
+
%% gexpr_test(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
%% Generate a guard test. At this stage we must be sure that we have
%% a proper boolean value here so wrap things with an true test if we
@@ -335,7 +352,8 @@ gexpr_test(E0, Bools0, St0) ->
#icall{anno=Anno,module=#c_literal{val=erlang},name=#c_literal{val=N},args=As} ->
Ar = length(As),
case erl_internal:type_test(N, Ar) orelse
- erl_internal:comp_op(N, Ar) of
+ erl_internal:comp_op(N, Ar) orelse
+ erl_internal:bool_op(N, Ar) of
true -> {E1,Eps0,Bools0,St1};
false ->
Lanno = Anno#a.anno,
@@ -496,14 +514,33 @@ expr({tuple,L,Es0}, St0) ->
expr({map,L,Es0}, St0) ->
% erl_lint should make sure only #{ K => V } are allowed
% in map construction.
- {Es1,Eps,St1} = map_pair_list(Es0, St0),
- A = lineno_anno(L, St1),
- {#c_map{anno=A,es=Es1},Eps,St1};
+ try map_pair_list(Es0, St0) of
+ {Es1,Eps,St1} ->
+ A = lineno_anno(L, St1),
+ {ann_c_map(A,Es1),Eps,St1}
+ catch
+ throw:{bad_map,Warning} ->
+ St = add_warning(L, Warning, St0),
+ LineAnno = lineno_anno(L, St),
+ As = [#c_literal{anno=LineAnno,val=badarg}],
+ {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
+ module=#c_literal{anno=LineAnno,val=erlang},
+ name=#c_literal{anno=LineAnno,val=error},
+ args=As},[],St}
+ end;
expr({map,L,M0,Es0}, St0) ->
- {M1,Mps,St1} = safe(M0, St0),
- {Es1,Eps,St2} = map_pair_list(Es0, St1),
- A = lineno_anno(L, St2),
- {#c_map{anno=A,var=M1,es=Es1},Mps++Eps,St2};
+ try expr_map(M0,Es0,lineno_anno(L, St0),St0) of
+ {_,_,_}=Res -> Res
+ catch
+ throw:{bad_map,Warning} ->
+ St = add_warning(L, Warning, St0),
+ LineAnno = lineno_anno(L, St),
+ As = [#c_literal{anno=LineAnno,val=badarg}],
+ {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
+ module=#c_literal{anno=LineAnno,val=erlang},
+ name=#c_literal{anno=LineAnno,val=error},
+ args=As},[],St}
+ end;
expr({bin,L,Es0}, St0) ->
try expr_bin(Es0, lineno_anno(L, St0), St0) of
{_,_,_}=Res -> Res
@@ -623,7 +660,7 @@ expr({call,Lc,{atom,Lf,F},As0}, St0) ->
Op = #c_var{anno=lineno_anno(Lf, St1),name={F,length(As1)}},
{#iapply{anno=#a{anno=lineno_anno(Lc, St1)},op=Op,args=As1},Aps,St1};
expr({call,L,FunExp,As0}, St0) ->
- {Fun,Fps,St1} = safe(FunExp, St0),
+ {Fun,Fps,St1} = safe_fun(length(As0), FunExp, St0),
{As1,Aps,St2} = safe_list(As0, St1),
Lanno = lineno_anno(L, St2),
{#iapply{anno=#a{anno=Lanno},op=Fun,args=As1},Fps ++ Aps,St2};
@@ -713,22 +750,58 @@ make_bool_switch_guard(L, E, V, T, F) ->
{clause,NegL,[V],[],[V]}
]}.
+expr_map(M0,Es0,A,St0) ->
+ {M1,Mps,St1} = safe(M0, St0),
+ case is_valid_map_src(M1) of
+ true ->
+ case {M1,Es0} of
+ {#c_var{}, []} ->
+ %% transform M#{} to is_map(M)
+ {Vpat,St2} = new_var(St1),
+ {Fpat,St3} = new_var(St2),
+ Cs = [#iclause{
+ anno=A,
+ pats=[Vpat],
+ guard=[#icall{anno=#a{anno=A},
+ module=#c_literal{anno=A,val=erlang},
+ name=#c_literal{anno=A,val=is_map},
+ args=[Vpat]}],
+ body=[Vpat]}],
+ Fc = fail_clause([Fpat], A, #c_literal{val=badarg}),
+ {#icase{anno=#a{anno=A},args=[M1],clauses=Cs,fc=Fc},Mps,St3};
+ {_,_} ->
+ {Es1,Eps,St2} = map_pair_list(Es0, St1),
+ {ann_c_map(A,M1,Es1),Mps++Eps,St2}
+ end;
+ false -> throw({bad_map,bad_map})
+ end.
+
+is_valid_map_src(#c_literal{val = M}) when is_map(M) -> true;
+is_valid_map_src(#c_map{}) -> true;
+is_valid_map_src(#c_var{}) -> true;
+is_valid_map_src(_) -> false.
+
map_pair_list(Es, St) ->
foldr(fun
({map_field_assoc,L,K0,V0}, {Ces,Esp,St0}) ->
{K,Ep0,St1} = safe(K0, St0),
+ ok = ensure_valid_map_key(K),
{V,Ep1,St2} = safe(V0, St1),
A = lineno_anno(L, St2),
Pair = #c_map_pair{op=#c_literal{val=assoc},anno=A,key=K,val=V},
{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2};
({map_field_exact,L,K0,V0}, {Ces,Esp,St0}) ->
{K,Ep0,St1} = safe(K0, St0),
+ ok = ensure_valid_map_key(K),
{V,Ep1,St2} = safe(V0, St1),
A = lineno_anno(L, St2),
Pair = #c_map_pair{op=#c_literal{val=exact},anno=A,key=K,val=V},
{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}
end, {[],[],St}, Es).
+ensure_valid_map_key(#c_literal{}) -> ok;
+ensure_valid_map_key(_) -> throw({bad_map,bad_map_key}).
+
%% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
try_exception(Ecs0, St0) ->
@@ -1408,6 +1481,15 @@ safe(E0, St0) ->
{Se,Sps,St2} = force_safe(E1, St1),
{Se,Eps ++ Sps,St2}.
+safe_fun(A0, E0, St0) ->
+ case safe(E0, St0) of
+ {#c_var{name={_,A1}}=E1,Eps,St1} when A1 =/= A0 ->
+ {V,St2} = new_var(St1),
+ {V,Eps ++ [#iset{var=V,arg=E1}],St2};
+ Result ->
+ Result
+ end.
+
safe_list(Es, St) ->
foldr(fun (E, {Ces,Esp,St0}) ->
{Ce,Ep,St1} = safe(E, St0),
@@ -1523,24 +1605,17 @@ pattern_alias_map_pair_patterns([Cv1,Cv2|Cvs]) ->
pattern_alias_map_pair_patterns([pat_alias(Cv1,Cv2)|Cvs]).
pattern_map_pair({map_field_exact,L,K,V}, St) ->
- %% FIXME: Better way to construct literals? or missing case
- %% {Key,_,_} = expr(K, St),
- Key = case K of
- {bin,L,Es0} ->
- case constant_bin(Es0) of
- error ->
- throw(badmatch);
- Bin ->
- #c_literal{anno=lineno_anno(L,St),val=Bin}
- end;
+ case expr(K,St) of
+ {#c_literal{}=Key,_,_} ->
+ #c_map_pair{anno=lineno_anno(L, St),
+ op=#c_literal{val=exact},
+ key=Key,
+ val=pattern(V, St)};
_ ->
- pattern(K,St)
- end,
- #c_map_pair{anno=lineno_anno(L, St),
- op=#c_literal{val=exact},
- key=Key,
- val=pattern(V, St)}.
-
+ %% this will throw a cryptic error message
+ %% but it is better than nothing
+ throw(nomatch)
+ end.
%% pat_bin([BinElement], State) -> [BinSeg].
@@ -1756,13 +1831,16 @@ uexpr(#iletrec{anno=A,defs=Fs0,body=B0}, Ks, St0) ->
{B1,St2} = uexprs(B0, Ks, St1),
Used = used_in_any(map(fun ({_,F}) -> F end, Fs1) ++ B1),
{#iletrec{anno=A#a{us=Used,ns=[]},defs=Fs1,body=B1},St2};
-uexpr(#icase{anno=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
+uexpr(#icase{anno=#a{anno=Anno}=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
%% As0 will never generate new variables.
{As1,St1} = uexpr_list(As0, Ks, St0),
{Cs1,St2} = uclauses(Cs0, Ks, St1),
{Fc1,St3} = uclause(Fc0, Ks, St2),
Used = union(used_in_any(As1), used_in_any(Cs1)),
- New = new_in_all(Cs1),
+ New = case member(list_comprehension, Anno) of
+ true -> [];
+ false -> new_in_all(Cs1)
+ end,
{#icase{anno=A#a{us=Used,ns=New},args=As1,clauses=Cs1,fc=Fc1},St3};
uexpr(#ifun{anno=A0,id=Id,vars=As,clauses=Cs0,fc=Fc0,name=Name}, Ks0, St0) ->
Avs = lit_list_vars(As),
@@ -2137,6 +2215,8 @@ lit_vars(Lit) -> lit_vars(Lit, []).
lit_vars(#c_cons{hd=H,tl=T}, Vs) -> lit_vars(H, lit_vars(T, Vs));
lit_vars(#c_tuple{es=Es}, Vs) -> lit_list_vars(Es, Vs);
+lit_vars(#c_map{arg=V,es=Es}, Vs) -> lit_vars(V, lit_list_vars(Es, Vs));
+lit_vars(#c_map_pair{key=K,val=V}, Vs) -> lit_vars(K, lit_vars(V, Vs));
lit_vars(#c_var{name=V}, Vs) -> add_element(V, Vs);
lit_vars(_, Vs) -> Vs. %These are atomic
@@ -2227,7 +2307,11 @@ is_simple_list(Es) -> lists:all(fun is_simple/1, Es).
format_error(nomatch) ->
"pattern cannot possibly match";
format_error(bad_binary) ->
- "binary construction will fail because of a type mismatch".
+ "binary construction will fail because of a type mismatch";
+format_error(bad_map_key) ->
+ "map construction will fail because of none literal key (large binaries are not literals)";
+format_error(bad_map) ->
+ "map construction will fail because of a type mismatch".
add_warning(Line, Term, #core{ws=Ws,file=[{file,File}]}=St) when Line >= 0 ->
St#core{ws=[{File,[{location(Line),?MODULE,Term}]}|Ws]};
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 5675572092..40d2f72b4c 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -272,9 +272,18 @@ expr(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub, St0) ->
expr(#c_tuple{anno=A,es=Ces}, Sub, St0) ->
{Kes,Ep,St1} = atomic_list(Ces, Sub, St0),
{#k_tuple{anno=A,es=Kes},Ep,St1};
-expr(#c_map{anno=A,var=Var0,es=Ces}, Sub, St0) ->
- {Var,[],St1} = expr(Var0, Sub, St0),
- map_split_pairs(A, Var, Ces, Sub, St1);
+expr(#c_map{anno=A,arg=Var,es=Ces}, Sub, St0) ->
+ try expr_map(A,Var,Ces,Sub,St0) of
+ {_,_,_}=Res -> Res
+ catch
+ throw:bad_map ->
+ St1 = add_warning(get_line(A), bad_map, A, St0),
+ Erl = #c_literal{val=erlang},
+ Name = #c_literal{val=error},
+ Args = [#c_literal{val=badarg}],
+ Error = #c_call{anno=A,module=Erl,name=Name,args=Args},
+ expr(Error, Sub, St1)
+ end;
expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->
try atomic_bin(Cv, Sub, St0) of
{Kv,Ep,St1} ->
@@ -496,6 +505,21 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) ->
translate_fc(Args) ->
[#c_literal{val=function_clause},make_list(Args)].
+expr_map(A,Var0,Ces,Sub,St0) ->
+ %% An extra pass of validation of Map src because of inlining
+ {Var,Mps,St1} = expr(Var0, Sub, St0),
+ case is_valid_map_src(Var) of
+ true ->
+ {Km,Eps,St2} = map_split_pairs(A, Var, Ces, Sub, St1),
+ {Km,Eps++Mps,St2};
+ false -> throw(bad_map)
+ end.
+
+is_valid_map_src(#k_map{}) -> true;
+is_valid_map_src(#k_literal{val=M}) when is_map(M) -> true;
+is_valid_map_src(#k_var{}) -> true;
+is_valid_map_src(_) -> false.
+
map_split_pairs(A, Var, Ces, Sub, St0) ->
%% two steps
%% 1. force variables
@@ -519,7 +543,7 @@ map_split_pairs(A, Var, Ces, Sub, St0) ->
Kes1 = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc],
{Mvar,Em,St2} = force_atomic(#k_map{anno=A,op=assoc,var=Var,es=Kes1},St1),
Kes2 = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact],
- {#k_map{anno=A,op=exact,var=Mvar,es=Kes2},Em ++ Esp,St2}
+ {#k_map{anno=A,op=exact,var=Mvar,es=Kes2},Esp ++ Em,St2}
end.
@@ -557,8 +581,7 @@ map_key_clean(#k_literal{val=V}) -> {k_literal,V};
map_key_clean(#k_int{val=V}) -> {k_int,V};
map_key_clean(#k_float{val=V}) -> {k_float,V};
map_key_clean(#k_atom{val=V}) -> {k_atom,V};
-map_key_clean(#k_nil{}) -> k_nil;
-map_key_clean(#k_var{name=V}) -> {k_var,V}.
+map_key_clean(#k_nil{}) -> k_nil.
%% call_type(Module, Function, Arity) -> call | bif | apply | error.
@@ -1986,7 +2009,9 @@ format_error(nomatch_shadow) ->
format_error(bad_call) ->
"invalid module and/or function name; this call will always fail";
format_error(bad_segment_size) ->
- "binary construction will fail because of a type mismatch".
+ "binary construction will fail because of a type mismatch";
+format_error(bad_map) ->
+ "map construction will fail because of a type mismatch".
add_warning(none, Term, Anno, #kern{ws=Ws}=St) ->
File = get_file(Anno),
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index b4e486f97c..b33eba50eb 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -104,20 +104,26 @@ format_1(#k_tuple{es=Es}, Ctxt) ->
format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
$}
];
-format_1(#k_map{var=#k_var{}=Var,es=Es}, Ctxt) ->
- [$~,${,
+format_1(#k_map{var=#k_literal{val=M},op=assoc,es=Es}, Ctxt) when is_map(M), map_size(M) =:= 0 ->
+ ["~{",
format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
- " | ",format_1(Var, Ctxt),
- $},$~
+ "}~"
];
-format_1(#k_map{op=assoc,es=Es}, Ctxt) ->
+format_1(#k_map{var=#k_literal{val=M},op=exact,es=Es}, Ctxt) when is_map(M), map_size(M) =:= 0 ->
+ ["::{",
+ format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ "}::"
+ ];
+format_1(#k_map{var=Var,op=assoc,es=Es}, Ctxt) ->
["~{",
format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ " | ",format_1(Var, Ctxt),
"}~"
];
-format_1(#k_map{es=Es}, Ctxt) ->
+format_1(#k_map{var=Var,op=exact,es=Es}, Ctxt) ->
["::{",
format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ " | ",format_1(Var, Ctxt),
"}::"
];
format_1(#k_map_pair{key=K,val=V}, Ctxt) ->
diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl
index c4f54a7970..cd4b5fd674 100644
--- a/lib/compiler/src/v3_life.erl
+++ b/lib/compiler/src/v3_life.erl
@@ -324,8 +324,7 @@ type(k_binary) -> binary;
type(k_bin_seg) -> bin_seg;
type(k_bin_int) -> bin_int;
type(k_bin_end) -> bin_end;
-type(k_map) -> map;
-type(k_map_pair) -> map_pair.
+type(k_map) -> map.
%% variable(Klit) -> Lit.
%% var_list([Klit]) -> [Lit].
diff --git a/lib/compiler/test/andor_SUITE.erl b/lib/compiler/test/andor_SUITE.erl
index 7bef0aa27c..b5408ecd8f 100644
--- a/lib/compiler/test/andor_SUITE.erl
+++ b/lib/compiler/test/andor_SUITE.erl
@@ -129,6 +129,10 @@ t_case_y(X, Y, Z) ->
Y =:= 100
end.
+-define(GUARD(E), if E -> true;
+ true -> false
+ end).
+
t_and_or(Config) when is_list(Config) ->
?line true = true and true,
?line false = true and false,
@@ -160,11 +164,16 @@ t_and_or(Config) when is_list(Config) ->
?line true = false or id(true),
?line false = false or id(false),
- ok.
+ True = id(true),
--define(GUARD(E), if E -> true;
- true -> false
- end).
+ false = ?GUARD(erlang:'and'(bar, True)),
+ false = ?GUARD(erlang:'or'(bar, True)),
+ false = ?GUARD(erlang:'not'(erlang:'and'(bar, True))),
+ false = ?GUARD(erlang:'not'(erlang:'not'(erlang:'and'(bar, True)))),
+
+ true = (fun (X = true) when X or true or X -> true end)(True),
+
+ ok.
t_andalso(Config) when is_list(Config) ->
Bs = [true,false],
diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl
index aa222c48de..428ad65364 100644
--- a/lib/compiler/test/core_SUITE.erl
+++ b/lib/compiler/test/core_SUITE.erl
@@ -24,7 +24,7 @@
dehydrated_itracer/1,nested_tries/1,
seq_in_guard/1,make_effect_seq/1,eval_is_boolean/1,
unsafe_case/1,nomatch_shadow/1,reversed_annos/1,
- map_core_test/1,eval_case/1]).
+ map_core_test/1,eval_case/1,bad_boolean_guard/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -50,7 +50,7 @@ groups() ->
[{p,test_lib:parallel(),
[dehydrated_itracer,nested_tries,seq_in_guard,make_effect_seq,
eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos,
- map_core_test,eval_case
+ map_core_test,eval_case,bad_boolean_guard
]}].
@@ -77,6 +77,7 @@ end_per_group(_GroupName, Config) ->
?comp(reversed_annos).
?comp(map_core_test).
?comp(eval_case).
+?comp(bad_boolean_guard).
try_it(Mod, Conf) ->
Src = filename:join(?config(data_dir, Conf), atom_to_list(Mod)),
diff --git a/lib/compiler/test/core_SUITE_data/bad_boolean_guard.core b/lib/compiler/test/core_SUITE_data/bad_boolean_guard.core
new file mode 100644
index 0000000000..318f8e3dc7
--- /dev/null
+++ b/lib/compiler/test/core_SUITE_data/bad_boolean_guard.core
@@ -0,0 +1,32 @@
+module 'bad_boolean_guard' ['bad_boolean_guard'/0,
+ 'module_info'/0,
+ 'module_info'/1]
+ attributes []
+'bad_boolean_guard'/0 =
+ fun () ->
+ apply 'f'/1
+ ('true')
+'f'/1 =
+ fun (_X_cor0) ->
+ case _X_cor0 of
+ <X>
+ when try
+ call 'erlang':'and'
+ ('bad', _X_cor0)
+ of <Try> ->
+ Try
+ catch <T,R> ->
+ 'false' ->
+ 'not_ok'
+ <_X_cor3> when 'true' ->
+ 'ok'
+ end
+'module_info'/0 =
+ fun () ->
+ call 'erlang':'get_module_info'
+ ('bad_boolean_guard')
+'module_info'/1 =
+ fun (_X_cor0) ->
+ call 'erlang':'get_module_info'
+ ('bad_boolean_guard', _X_cor0)
+end \ No newline at end of file
diff --git a/lib/compiler/test/core_SUITE_data/map_core_test.core b/lib/compiler/test/core_SUITE_data/map_core_test.core
index 7ece8a8bbd..2aa853d450 100644
--- a/lib/compiler/test/core_SUITE_data/map_core_test.core
+++ b/lib/compiler/test/core_SUITE_data/map_core_test.core
@@ -67,7 +67,7 @@ module 'map_core_test' ['map_core_test'/0,
(Val, V)
in let <_cor5> =
%% Line 21
- M~{~<1337,_cor4>,~<'val',_cor2>}~
+ ~{~<1337,_cor4>,~<'val',_cor2>|M}~
in %% Line 21
apply 'call'/2
(_cor5, Vs)
@@ -92,4 +92,4 @@ module 'map_core_test' ['map_core_test'/0,
fun (_cor0) ->
call 'erlang':'get_module_info'
('map_core_test', _cor0)
-end \ No newline at end of file
+end
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index 8151dc1b16..6a7036d728 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -23,7 +23,7 @@
t_element/1,setelement/1,t_length/1,append/1,t_apply/1,bifs/1,
eq/1,nested_call_in_case/1,guard_try_catch/1,coverage/1,
unused_multiple_values_error/1,unused_multiple_values/1,
- multiple_aliases/1]).
+ multiple_aliases/1,redundant_boolean_clauses/1,mixed_matching_clauses/1]).
-export([foo/0,foo/1,foo/2,foo/3]).
@@ -40,7 +40,7 @@ groups() ->
[t_element,setelement,t_length,append,t_apply,bifs,
eq,nested_call_in_case,guard_try_catch,coverage,
unused_multiple_values_error,unused_multiple_values,
- multiple_aliases]}].
+ multiple_aliases,redundant_boolean_clauses,mixed_matching_clauses]}].
init_per_suite(Config) ->
@@ -365,4 +365,23 @@ run_once() ->
ok.
+redundant_boolean_clauses(Config) when is_list(Config) ->
+ X = id(0),
+ yes = case X == 0 of
+ false -> no;
+ false -> no;
+ true -> yes
+ end.
+
+mixed_matching_clauses(Config) when is_list(Config) ->
+ 0 = case #{} of
+ #{} -> 0;
+ a -> 1
+ end,
+ 0 = case <<>> of
+ <<>> -> 0;
+ a -> 1
+ end,
+ ok.
+
id(I) -> I.
diff --git a/lib/compiler/test/error_SUITE.erl b/lib/compiler/test/error_SUITE.erl
index 5cdf429a5f..bd877bb528 100644
--- a/lib/compiler/test/error_SUITE.erl
+++ b/lib/compiler/test/error_SUITE.erl
@@ -23,7 +23,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
head_mismatch_line/1,warnings_as_errors/1, bif_clashes/1,
- transforms/1,forbidden_maps/1]).
+ transforms/1,forbidden_maps/1,bad_utf8/1]).
%% Used by transforms/1 test case.
-export([parse_transform/2]).
@@ -36,7 +36,8 @@ all() ->
groups() ->
[{p,test_lib:parallel(),
- [head_mismatch_line,warnings_as_errors,bif_clashes,transforms,forbidden_maps]}].
+ [head_mismatch_line,warnings_as_errors,bif_clashes,
+ transforms,forbidden_maps,bad_utf8]}].
init_per_suite(Config) ->
Config.
@@ -254,6 +255,23 @@ forbidden_maps(Config) when is_list(Config) ->
[] = run2(Config, Ts1),
ok.
+bad_utf8(Config) ->
+ Ts = [{bad_utf8,
+ %% If coding is specified explicitly as utf-8, there should be
+ %% a compilation error; we must not fallback to parsing the
+ %% file in latin-1 mode.
+ <<"%% coding: utf-8
+ %% Bj",246,"rn
+ t() -> \"",246,"\".
+ ">>,
+ [],
+ {error,[{2,epp,cannot_parse},
+ {2,file_io_server,invalid_unicode}],
+ []}
+ }],
+ [] = run2(Config, Ts),
+ ok.
+
run(Config, Tests) ->
?line File = test_filename(Config),
@@ -318,6 +336,7 @@ run_test(Test0, File, Warnings, WriteBeam) ->
?line compile:file(File, [binary,report|Warnings]),
%% Test result of compilation.
+ io:format("~p\n", [Opts]),
?line Res = case compile:file(File, Opts) of
{ok,Mod,_,[{_File,Ws}]} ->
%io:format("compile:file(~s,~p) ->~n~p~n",
@@ -335,6 +354,11 @@ run_test(Test0, File, Warnings, WriteBeam) ->
%io:format("compile:file(~s,~p) ->~n~p~n",
% [File,Opts,_ZZ]),
{error,Es,Ws};
+ {error,[{XFile,Es1},{XFile,Es2}],Ws} = _ZZ
+ when is_list(XFile) ->
+ %io:format("compile:file(~s,~p) ->~n~p~n",
+ % [File,Opts,_ZZ]),
+ {error,Es1++Es2,Ws};
{error,Es,[{_File,Ws}]} = _ZZ->
%io:format("compile:file(~s,~p) ->~n~p~n",
% [File,Opts,_ZZ]),
diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl
index e35692efd1..25b7f677b5 100644
--- a/lib/compiler/test/fun_SUITE.erl
+++ b/lib/compiler/test/fun_SUITE.erl
@@ -21,7 +21,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1,
- external/1,eep37/1]).
+ external/1,eep37/1,badarity/1]).
%% Internal export.
-export([call_me/1]).
@@ -32,7 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [test1,overwritten_fun,otp_7202,bif_fun,external,eep37].
+ [test1,overwritten_fun,otp_7202,bif_fun,external,eep37,badarity].
groups() ->
[].
@@ -206,5 +206,9 @@ eep37(Config) when is_list(Config) ->
50 = UnusedName(8),
ok.
+badarity(Config) when is_list(Config) ->
+ {'EXIT',{{badarity,{_,[]}},_}} = (catch (fun badarity/1)()),
+ ok.
+
id(I) ->
I.
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index a0a9bb7ddd..eb205d09a7 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -33,7 +33,7 @@
tricky/1,rel_ops/1,literal_type_tests/1,
basic_andalso_orelse/1,traverse_dcd/1,
check_qlc_hrl/1,andalso_semi/1,t_tuple_size/1,binary_part/1,
- bad_constants/1]).
+ bad_constants/1,bad_guards/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -50,7 +50,7 @@ groups() ->
t_is_boolean,is_function_2,tricky,rel_ops,
literal_type_tests,basic_andalso_orelse,traverse_dcd,
check_qlc_hrl,andalso_semi,t_tuple_size,binary_part,
- bad_constants]}].
+ bad_constants,bad_guards]}].
init_per_suite(Config) ->
Config.
@@ -1023,6 +1023,10 @@ is_function_2(Config) when is_list(Config) ->
true = is_function(id(fun() -> ok end), 0),
false = is_function(id(fun ?MODULE:all/1), 0),
false = is_function(id(fun() -> ok end), 1),
+ {'EXIT',{badarg,_}} =
+ (catch is_function(id(fun() -> ok end), -1) orelse error),
+ {'EXIT',{badarg,_}} =
+ (catch is_function(id(fun() -> ok end), '') orelse error),
F = fun(_) -> ok end,
if
@@ -1550,6 +1554,10 @@ bad_constants(Config) when is_list(Config) ->
?line ?FAILING(3.14),
ok.
+bad_guards(Config) when is_list(Config) ->
+ if erlang:float(self()); true -> ok end,
+ ok.
+
%% Call this function to turn off constant propagation.
id(I) -> I.
diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl
index f5948504b3..398398a397 100644
--- a/lib/compiler/test/lc_SUITE.erl
+++ b/lib/compiler/test/lc_SUITE.erl
@@ -23,7 +23,7 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
basic/1,deeply_nested/1,no_generator/1,
- empty_generator/1]).
+ empty_generator/1,no_export/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -31,7 +31,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
test_lib:recompile(?MODULE),
- [basic, deeply_nested, no_generator, empty_generator].
+ [basic, deeply_nested, no_generator, empty_generator, no_export].
groups() ->
[].
@@ -177,6 +177,10 @@ empty_generator(Config) when is_list(Config) ->
?line [] = [X || {X} <- [], (false or (X/0 > 3))],
ok.
+no_export(Config) when is_list(Config) ->
+ [] = [ _X = a || false ] ++ [ _X = a || false ],
+ ok.
+
id(I) -> I.
fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Args,_}|_]}}) -> ok;
diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl
index b4baef461b..403b7e8405 100644
--- a/lib/compiler/test/map_SUITE.erl
+++ b/lib/compiler/test/map_SUITE.erl
@@ -40,9 +40,13 @@
t_build_and_match_over_alloc/1,
t_build_and_match_empty_val/1,
t_build_and_match_val/1,
+ t_build_and_match_nil/1,
+ t_build_and_match_structure/1,
%% errors in 17.0-rc1
- t_update_values/1
+ t_update_values/1,
+ t_expand_map_update/1,
+ t_export/1
]).
suite() -> [].
@@ -66,9 +70,13 @@ all() -> [
t_build_and_match_over_alloc,
t_build_and_match_empty_val,
t_build_and_match_val,
+ t_build_and_match_nil,
+ t_build_and_match_structure,
%% errors in 17.0-rc1
- t_update_values
+ t_update_values,
+ t_expand_map_update,
+ t_export
].
groups() -> [].
@@ -105,12 +113,21 @@ t_build_and_match_literals(Config) when is_list(Config) ->
M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} =
id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}),
+ %% map key
+ #{ #{} := 42 } = id(#{ #{} => 42 }),
+ #{ #{ "a" => 3 } := 42 } = id(#{ #{ "a" => 3} => 42 }),
+
+ %% nil key
+ #{[]:=ok,1:=2} = id(#{[]=>ok,1=>2}),
+
%% error case
{'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))),
{'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))),
{'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))),
{'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))),
{'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))),
+ {'EXIT',{badarg,_}} = (catch id(#{<<0:258>> =>"three"})),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{#{"a"=>42} := 3}=id(#{#{"a"=>3}=>42}))),
ok.
t_build_and_match_aliasing(Config) when is_list(Config) ->
@@ -187,7 +204,9 @@ loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
t_update_map_expressions(Config) when is_list(Config) ->
M = maps:new(),
- #{ a := 1 } = M#{a => 1},
+ X = id(fondue),
+ M1 = #{ a := 1 } = M#{a => 1},
+ #{ b := {X} } = M1#{ a := 1, b => {X} },
#{ b := 2 } = (maps:new())#{ b => 2 },
@@ -225,7 +244,8 @@ t_update_assoc(Config) when is_list(Config) ->
%% Errors cases.
BadMap = id(badmap),
{'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}),
-
+ {'EXIT',{badarg,_}} = (catch <<>>#{nonexisting=>val}),
+ {'EXIT',{badarg,_}} = (catch M0#{<<0:257>> => val}), %% limitation
ok.
t_update_exact(Config) when is_list(Config) ->
@@ -253,6 +273,8 @@ t_update_exact(Config) when is_list(Config) ->
{'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}),
{'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}),
{'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),
+ {'EXIT',{badarg,_}} = (catch <<>>#{nonexisting:=val}),
+ {'EXIT',{badarg,_}} = (catch M0#{<<0:257>> := val}), %% limitation
ok.
t_update_values(Config) when is_list(Config) ->
@@ -273,20 +295,39 @@ t_update_values(Config) when is_list(Config) ->
end, {none, none, #{val1=>none,val2=>none}},List),
ok.
+t_expand_map_update(Config) when is_list(Config) ->
+ M = #{<<"hello">> => <<"world">>}#{<<"hello">> := <<"les gens">>},
+ #{<<"hello">> := <<"les gens">>} = M,
+ ok.
+
+t_export(Config) when is_list(Config) ->
+ Raclette = id(#{}),
+ case brie of brie -> Fromage = Raclette end,
+ Raclette = Fromage#{},
+ ok.
+
check_val(#{val1:=V1, val2:=V2},V1,V2) -> ok.
get_val(#{ "wazzup" := _, val := V}) -> V;
get_val(#{ val := V }) -> {some_val, V}.
t_guard_bifs(Config) when is_list(Config) ->
+ true = map_guard_empty(),
+ true = map_guard_empty_2(),
true = map_guard_head(#{a=>1}),
false = map_guard_head([]),
true = map_guard_body(#{a=>1}),
false = map_guard_body({}),
true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }),
false = map_guard_pattern("list"),
+ true = map_guard_tautology(),
+ true = map_guard_ill_map_size(),
ok.
+map_guard_empty() when is_map(#{}); false -> true.
+
+map_guard_empty_2() when true; #{} andalso false -> true.
+
map_guard_head(M) when is_map(M) -> true;
map_guard_head(_) -> false.
@@ -295,6 +336,10 @@ map_guard_body(M) -> is_map(M).
map_guard_pattern(#{}) -> true;
map_guard_pattern(_) -> false.
+map_guard_tautology() when #{} =:= #{}; true -> true.
+
+map_guard_ill_map_size() when true; map_size(0) -> true.
+
t_guard_sequence(Config) when is_list(Config) ->
{1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}),
{2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}),
@@ -529,6 +574,32 @@ t_build_and_match_val(Config) when is_list(Config) ->
test_server:fail({no_match, Other})
end.
+t_build_and_match_nil(Config) when is_list(Config) ->
+ %% literals removed the coverage
+ V1 = id(cookie),
+ V2 = id(cake),
+ V3 = id(crisps),
+
+ #{ [] := V1, "treat" := V2, {your,treat} := V3 } = id(#{
+ {your,treat} => V3,
+ "treat" => V2,
+ [] => V1 }),
+ #{ [] := V3, [] := V3 } = id(#{ [] => V1, [] => V3 }),
+ ok.
+
+t_build_and_match_structure(Config) when is_list(Config) ->
+ V2 = id("it"),
+ S = id([42,{"hi", "=)", #{ "a" => 42, any => any, val => "get_" ++ V2}}]),
+
+ %% match deep map values
+ V2 = case S of
+ [42,{"hi",_, #{ "a" := 42, val := "get_" ++ V1, any := _ }}] -> V1
+ end,
+ %% match deep map
+ ok = case S of
+ [42,{"hi",_, #{ }}] -> ok
+ end,
+ ok.
%% Use this function to avoid compile-time evaluation of an expression.
id(I) -> I.
diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl
index ec49267ded..00a6e900d4 100644
--- a/lib/compiler/test/receive_SUITE.erl
+++ b/lib/compiler/test/receive_SUITE.erl
@@ -257,6 +257,7 @@ wait(Config) when is_list(Config) ->
self() ! <<42>>,
<<42>> = wait_1(r, 1, 2),
{1,2,3} = wait_1(1, 2, 3),
+ {'EXIT',{timeout_value,_}} = (catch receive after [] -> timeout end),
ok.
wait_1(r, _, _) ->
diff --git a/lib/compiler/test/record_SUITE.erl b/lib/compiler/test/record_SUITE.erl
index c9f5a2053e..f736e14bf6 100644
--- a/lib/compiler/test/record_SUITE.erl
+++ b/lib/compiler/test/record_SUITE.erl
@@ -369,6 +369,14 @@ record_test_3(Config) when is_list(Config) ->
?line false = is_record(id(#barf{}), id(barf), id(42)),
?line false = is_record(id(#barf{}), id(foo), id(6)),
+ Rec = id(#barf{}),
+ Good = id(barf),
+ Bad = id(foo),
+ Size = id(6),
+
+ true = is_record(Rec, Good, Size) orelse error,
+ error = is_record(Rec, Bad, Size) orelse error,
+
ok.
record_access_in_guards(Config) when is_list(Config) ->
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index 16d15a59e5..ad4ad91f74 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -37,7 +37,9 @@
-export([pattern/1,pattern2/1,pattern3/1,pattern4/1,
guard/1,bad_arith/1,bool_cases/1,bad_apply/1,
- files/1,effect/1,bin_opt_info/1,bin_construction/1, comprehensions/1]).
+ files/1,effect/1,bin_opt_info/1,bin_construction/1,
+ comprehensions/1,maps/1,redundant_boolean_clauses/1,
+ latin1_fallback/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(2)).
@@ -61,7 +63,8 @@ groups() ->
[{p,test_lib:parallel(),
[pattern,pattern2,pattern3,pattern4,guard,
bad_arith,bool_cases,bad_apply,files,effect,
- bin_opt_info,bin_construction,comprehensions]}].
+ bin_opt_info,bin_construction,comprehensions,maps,
+ redundant_boolean_clauses,latin1_fallback]}].
init_per_suite(Config) ->
Config.
@@ -200,6 +203,8 @@ pattern4(Config) when is_list(Config) ->
[nowarn_unused_vars],
{warnings,
[{9,sys_core_fold,no_clause_match},
+ {11,sys_core_fold,nomatch_shadow},
+ {15,sys_core_fold,nomatch_shadow},
{18,sys_core_fold,no_clause_match},
{23,sys_core_fold,no_clause_match},
{33,sys_core_fold,no_clause_match}
@@ -552,6 +557,113 @@ comprehensions(Config) when is_list(Config) ->
run(Config, Ts),
ok.
+maps(Config) when is_list(Config) ->
+ Ts = [{bad_map,
+ <<"
+ t() ->
+ case maybe_map of
+ #{} -> ok;
+ not_map -> error
+ end.
+ x() ->
+ case true of
+ #{} -> error;
+ true -> ok
+ end.
+ ">>,
+ [],
+ {warnings,[{3,sys_core_fold,no_clause_match},
+ {9,sys_core_fold,nomatch_clause_type}]}},
+ {bad_map_src1,
+ <<"
+ t() ->
+ M = {a,[]},
+ {'EXIT',{badarg,_}} = (catch(M#{ a => 1})),
+ ok.
+ ">>,
+ [],
+ {warnings,[{4,v3_kernel,bad_map}]}},
+ {bad_map_src2,
+ <<"
+ t() ->
+ M = id({a,[]}),
+ {'EXIT',{badarg,_}} = (catch(M#{ a => 1})),
+ ok.
+ id(I) -> I.
+ ">>,
+ [inline],
+ {warnings,[{4,v3_kernel,bad_map}]}},
+ {bad_map_src3,
+ <<"
+ t() ->
+ {'EXIT',{badarg,_}} = (catch <<>>#{ a := 1}),
+ ok.
+ ">>,
+ [],
+ {warnings,[{3,v3_core,bad_map}]}},
+ {bad_map_literal_key,
+ <<"
+ t() ->
+ V = id(1),
+ M = id(#{ <<$h,$i>> => V }),
+ V = case M of
+ #{ <<0:257>> := Val } -> Val;
+ #{ <<$h,$i>> := Val } -> Val
+ end,
+ ok.
+ id(I) -> I.
+ ">>,
+ [],
+ {warnings,[{6,v3_core,nomatch}]}}],
+ run(Config, Ts),
+ ok.
+
+redundant_boolean_clauses(Config) when is_list(Config) ->
+ Ts = [{redundant_boolean_clauses,
+ <<"
+ t(X) ->
+ case X == 0 of
+ false -> no;
+ false -> no;
+ true -> yes
+ end.
+ ">>,
+ [],
+ {warnings,[{5,sys_core_fold,nomatch_shadow}]}}],
+ run(Config, Ts),
+ ok.
+
+latin1_fallback(Conf) when is_list(Conf) ->
+ DataDir = ?privdir,
+ IncFile = filename:join(DataDir, "include_me.hrl"),
+ file:write_file(IncFile, <<"%% ",246," in include file\n">>),
+ Ts1 = [{latin1_fallback1,
+ %% Test that the compiler fall backs to latin-1 with
+ %% a warning if a file has no encoding and does not
+ %% contain correct UTF-8 sequences.
+ <<"%% Bj",246,"rn
+ t(_) -> \"",246,"\";
+ t(x) -> ok.
+ ">>,
+ [],
+ {warnings,[{1,compile,reparsing_invalid_unicode},
+ {3,sys_core_fold,{nomatch_shadow,2}}]}}],
+ [] = run(Conf, Ts1),
+
+ Ts2 = [{latin1_fallback2,
+ %% Test that the compiler fall backs to latin-1 with
+ %% a warning if a file has no encoding and does not
+ %% contain correct UTF-8 sequences.
+ <<"
+
+ -include(\"include_me.hrl\").
+ ">>,
+ [],
+ {warnings,[{1,compile,reparsing_invalid_unicode}]}
+ }],
+ [] = run(Conf, Ts2),
+ ok.
+
%%%
%%% End of test cases.
%%%
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index cbdf57f177..c0c3d56472 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 4.9.4
+COMPILER_VSN = 5.0
diff --git a/lib/cosEvent/doc/src/notes.xml b/lib/cosEvent/doc/src/notes.xml
index df05f5634e..8f519447fc 100644
--- a/lib/cosEvent/doc/src/notes.xml
+++ b/lib/cosEvent/doc/src/notes.xml
@@ -32,7 +32,23 @@
<file>notes.xml</file>
</header>
- <section><title>cosEvent 2.1.14</title>
+ <section><title>cosEvent 2.1.15</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The default encoding of Erlang files has been changed
+ from ISO-8859-1 to UTF-8. </p> <p> The encoding of XML
+ files has also been changed to UTF-8. </p>
+ <p>
+ Own Id: OTP-10907</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosEvent 2.1.14</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosEvent/src/cosEvent.app.src b/lib/cosEvent/src/cosEvent.app.src
index c1cb9e0cc9..66b0d2e168 100644
--- a/lib/cosEvent/src/cosEvent.app.src
+++ b/lib/cosEvent/src/cosEvent.app.src
@@ -38,7 +38,8 @@
{registered, []},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosEventApp, []}}
+ {mod, {cosEventApp, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/cosEvent/vsn.mk b/lib/cosEvent/vsn.mk
index 6745bee079..40bf1ba49d 100644
--- a/lib/cosEvent/vsn.mk
+++ b/lib/cosEvent/vsn.mk
@@ -1,3 +1,3 @@
-COSEVENT_VSN = 2.1.14
+COSEVENT_VSN = 2.1.15
diff --git a/lib/cosEventDomain/doc/src/notes.xml b/lib/cosEventDomain/doc/src/notes.xml
index f88d101a69..2c3bf16411 100644
--- a/lib/cosEventDomain/doc/src/notes.xml
+++ b/lib/cosEventDomain/doc/src/notes.xml
@@ -31,7 +31,23 @@
<file>notes.xml</file>
</header>
- <section><title>cosEventDomain 1.1.13</title>
+ <section><title>cosEventDomain 1.1.14</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The default encoding of Erlang files has been changed
+ from ISO-8859-1 to UTF-8. </p> <p> The encoding of XML
+ files has also been changed to UTF-8. </p>
+ <p>
+ Own Id: OTP-10907</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosEventDomain 1.1.13</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosEventDomain/src/cosEventDomain.app.src b/lib/cosEventDomain/src/cosEventDomain.app.src
index e4307e1f99..60114b6a91 100644
--- a/lib/cosEventDomain/src/cosEventDomain.app.src
+++ b/lib/cosEventDomain/src/cosEventDomain.app.src
@@ -27,5 +27,7 @@
{registered, []},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosEventDomainApp, []}}
+ {mod, {cosEventDomainApp, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","kernel-3.0","erts-6.0",
+ "cosNotification-1.1.21"]}
]}.
diff --git a/lib/cosEventDomain/vsn.mk b/lib/cosEventDomain/vsn.mk
index e9cf92395a..6317ed3c22 100644
--- a/lib/cosEventDomain/vsn.mk
+++ b/lib/cosEventDomain/vsn.mk
@@ -1,3 +1,3 @@
-COSEVENTDOMAIN_VSN = 1.1.13
+COSEVENTDOMAIN_VSN = 1.1.14
diff --git a/lib/cosFileTransfer/doc/src/notes.xml b/lib/cosFileTransfer/doc/src/notes.xml
index 5cca5e307b..1d0c826d40 100644
--- a/lib/cosFileTransfer/doc/src/notes.xml
+++ b/lib/cosFileTransfer/doc/src/notes.xml
@@ -30,7 +30,23 @@
<file>notes.xml</file>
</header>
- <section><title>cosFileTransfer 1.1.15</title>
+ <section><title>cosFileTransfer 1.1.16</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The default encoding of Erlang files has been changed
+ from ISO-8859-1 to UTF-8. </p> <p> The encoding of XML
+ files has also been changed to UTF-8. </p>
+ <p>
+ Own Id: OTP-10907</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosFileTransfer 1.1.15</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosFileTransfer/src/cosFileTransfer.app.src b/lib/cosFileTransfer/src/cosFileTransfer.app.src
index 31d94b6f0d..21226b0c6b 100644
--- a/lib/cosFileTransfer/src/cosFileTransfer.app.src
+++ b/lib/cosFileTransfer/src/cosFileTransfer.app.src
@@ -36,6 +36,8 @@
{registered, []},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosFileTransferApp, []}}
+ {mod, {cosFileTransferApp, []}},
+ {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","orber-3.6.27","kernel-3.0",
+ "inets-5.10","erts-6.0","cosProperty-1.1.17"]}
]}.
diff --git a/lib/cosFileTransfer/vsn.mk b/lib/cosFileTransfer/vsn.mk
index cf33926334..f52a1bd800 100644
--- a/lib/cosFileTransfer/vsn.mk
+++ b/lib/cosFileTransfer/vsn.mk
@@ -1 +1 @@
-COSFILETRANSFER_VSN = 1.1.15
+COSFILETRANSFER_VSN = 1.1.16
diff --git a/lib/cosNotification/doc/src/notes.xml b/lib/cosNotification/doc/src/notes.xml
index b700e0984c..2e4f922142 100644
--- a/lib/cosNotification/doc/src/notes.xml
+++ b/lib/cosNotification/doc/src/notes.xml
@@ -31,7 +31,23 @@
<file>notes.xml</file>
</header>
- <section><title>cosNotification 1.1.20</title>
+ <section><title>cosNotification 1.1.21</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The default encoding of Erlang files has been changed
+ from ISO-8859-1 to UTF-8. </p> <p> The encoding of XML
+ files has also been changed to UTF-8. </p>
+ <p>
+ Own Id: OTP-10907</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosNotification 1.1.20</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosNotification/src/cosNotification.app.src b/lib/cosNotification/src/cosNotification.app.src
index 04beac36e8..ad02eb4421 100644
--- a/lib/cosNotification/src/cosNotification.app.src
+++ b/lib/cosNotification/src/cosNotification.app.src
@@ -116,5 +116,7 @@
{registered, [cosNotificationSup, oe_cosNotificationFactory]},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosNotificationApp, []}}
+ {mod, {cosNotificationApp, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","kernel-3.0","erts-6.0",
+ "cosTime-1.1.14","cosEvent-2.1.15"]}
]}.
diff --git a/lib/cosNotification/vsn.mk b/lib/cosNotification/vsn.mk
index ea59800164..28d6ec71bf 100644
--- a/lib/cosNotification/vsn.mk
+++ b/lib/cosNotification/vsn.mk
@@ -1,2 +1,2 @@
-COSNOTIFICATION_VSN = 1.1.20
+COSNOTIFICATION_VSN = 1.1.21
diff --git a/lib/cosProperty/doc/src/notes.xml b/lib/cosProperty/doc/src/notes.xml
index 22b1a73d29..739f41617f 100644
--- a/lib/cosProperty/doc/src/notes.xml
+++ b/lib/cosProperty/doc/src/notes.xml
@@ -31,7 +31,23 @@
<file>notes.xml</file>
</header>
- <section><title>cosProperty 1.1.16</title>
+ <section><title>cosProperty 1.1.17</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The default encoding of Erlang files has been changed
+ from ISO-8859-1 to UTF-8. </p> <p> The encoding of XML
+ files has also been changed to UTF-8. </p>
+ <p>
+ Own Id: OTP-10907</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosProperty 1.1.16</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosProperty/src/cosProperty.app.src b/lib/cosProperty/src/cosProperty.app.src
index 3099e904f7..b977bb5984 100644
--- a/lib/cosProperty/src/cosProperty.app.src
+++ b/lib/cosProperty/src/cosProperty.app.src
@@ -41,5 +41,7 @@
{registered, [oe_cosPropertySup]},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosProperty, []}}
+ {mod, {cosProperty, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","mnesia-4.12",
+ "kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/cosProperty/vsn.mk b/lib/cosProperty/vsn.mk
index ac7820216e..0f546a2da8 100644
--- a/lib/cosProperty/vsn.mk
+++ b/lib/cosProperty/vsn.mk
@@ -1,2 +1,2 @@
-COSPROPERTY_VSN = 1.1.16
+COSPROPERTY_VSN = 1.1.17
diff --git a/lib/cosTime/doc/src/notes.xml b/lib/cosTime/doc/src/notes.xml
index c1d000a5ff..f218f19a6b 100644
--- a/lib/cosTime/doc/src/notes.xml
+++ b/lib/cosTime/doc/src/notes.xml
@@ -32,7 +32,23 @@
<file>notes.xml</file>
</header>
- <section><title>cosTime 1.1.13</title>
+ <section><title>cosTime 1.1.14</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The default encoding of Erlang files has been changed
+ from ISO-8859-1 to UTF-8. </p> <p> The encoding of XML
+ files has also been changed to UTF-8. </p>
+ <p>
+ Own Id: OTP-10907</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosTime 1.1.13</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosTime/src/cosTime.app.src b/lib/cosTime/src/cosTime.app.src
index 191ee5f3db..cd01de35cb 100644
--- a/lib/cosTime/src/cosTime.app.src
+++ b/lib/cosTime/src/cosTime.app.src
@@ -26,5 +26,7 @@
{registered, [oe_cosTimeSup, oe_cosTimerEventService]},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosTime, []}}
+ {mod, {cosTime, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","kernel-3.0","erts-6.0",
+ "cosEvent-2.1.15"]}
]}.
diff --git a/lib/cosTime/vsn.mk b/lib/cosTime/vsn.mk
index 02cd669222..9e9e5c0250 100644
--- a/lib/cosTime/vsn.mk
+++ b/lib/cosTime/vsn.mk
@@ -1,2 +1,3 @@
-COSTIME_VSN = 1.1.13
+COSTIME_VSN = 1.1.14
+
diff --git a/lib/cosTransactions/doc/src/notes.xml b/lib/cosTransactions/doc/src/notes.xml
index eab402aee8..4b20f23efb 100644
--- a/lib/cosTransactions/doc/src/notes.xml
+++ b/lib/cosTransactions/doc/src/notes.xml
@@ -32,7 +32,23 @@
<file>notes.xml</file>
</header>
- <section><title>cosTransactions 1.2.13</title>
+ <section><title>cosTransactions 1.2.14</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The default encoding of Erlang files has been changed
+ from ISO-8859-1 to UTF-8. </p> <p> The encoding of XML
+ files has also been changed to UTF-8. </p>
+ <p>
+ Own Id: OTP-10907</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosTransactions 1.2.13</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosTransactions/src/cosTransactions.app.src b/lib/cosTransactions/src/cosTransactions.app.src
index 52769b1711..6b99915ad6 100644
--- a/lib/cosTransactions/src/cosTransactions.app.src
+++ b/lib/cosTransactions/src/cosTransactions.app.src
@@ -39,5 +39,6 @@
{registered, [cosTransactions_sup, oe_cosTransactionsFactory]},
{applications, [orber, stdlib, kernel]},
{env, []},
- {mod, {cosTransactions, []}}
+ {mod, {cosTransactions, []}},
+ {runtime_dependencies, ["stdlib-2.0","orber-3.6.27","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/cosTransactions/vsn.mk b/lib/cosTransactions/vsn.mk
index 5414270a3d..7aed212523 100644
--- a/lib/cosTransactions/vsn.mk
+++ b/lib/cosTransactions/vsn.mk
@@ -1 +1 @@
-COSTRANSACTIONS_VSN = 1.2.13
+COSTRANSACTIONS_VSN = 1.2.14
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index fca08c4eed..948093d69c 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -215,6 +215,7 @@ static ERL_NIF_TERM des_cfb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
static ERL_NIF_TERM des_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM des_ede3_cfb_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(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_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -344,6 +345,7 @@ static ErlNifFunc nif_funcs[] = {
{"des_ecb_crypt", 3, des_ecb_crypt},
{"des_ede3_cbc_crypt", 6, des_ede3_cbc_crypt},
{"des_ede3_cfb_crypt_nif", 6, des_ede3_cfb_crypt_nif},
+ {"aes_cfb_8_crypt", 4, aes_cfb_8_crypt},
{"aes_cfb_128_crypt", 4, aes_cfb_128_crypt},
{"aes_ctr_encrypt", 3, aes_ctr_encrypt},
{"aes_ctr_decrypt", 3, aes_ctr_encrypt},
@@ -1382,6 +1384,7 @@ static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
ErlNifBinary key;
struct hmac_context* obj;
const EVP_MD *md;
+ ERL_NIF_TERM ret;
CHECK_OSE_CRYPTO();
@@ -1413,7 +1416,9 @@ static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
HMAC_CTX_init(&obj->ctx);
HMAC_Init(&obj->ctx, key.data, key.size, md);
- return enif_make_resource(env, obj);
+ ret = enif_make_resource(env, obj);
+ enif_release_resource(obj);
+ return ret;
}
static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -1600,6 +1605,30 @@ static ERL_NIF_TERM des_ede3_cfb_crypt_nif(ErlNifEnv* env, int argc, const ERL_N
#endif
}
+static ERL_NIF_TERM aes_cfb_8_crypt(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
+ || !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, 128, &aes_key);
+ AES_cfb8_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_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Key, IVec, Data, IsEncrypt) */
ErlNifBinary key, ivec, text;
@@ -2480,6 +2509,7 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
return enif_make_binary(env, &ret_bin);
}
else {
+ enif_release_binary(&ret_bin);
return atom_error;
}
}
@@ -2742,6 +2772,7 @@ static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_T
ret = enif_make_binary(env, &ret_bin);
}
else {
+ enif_release_binary(&ret_bin);
ret = atom_error;
}
}
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index e88bf01491..7712173ed8 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -128,7 +128,7 @@
<p><code>stream_cipher() = rc4 | aes_ctr </code></p>
- <p><code>block_cipher() = aes_cbc128 | aes_cfb128 | aes_ige256 | blowfish_cbc |
+ <p><code>block_cipher() = aes_cbc128 | aes_cfb8 | aes_cfb128 | aes_ige256 | blowfish_cbc |
blowfish_cfb64 | des_cbc | des_cfb | des3_cbc | des3_cbf
| des_ede3 | rc2_cbc </code></p>
@@ -152,7 +152,7 @@
Note that both md4 and md5 are recommended only for compatibility with existing applications.
</p>
<p><code> cipher_algorithms() = des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 |
- blowfish_cbc | blowfish_cfb64 | aes_cbc128 | aes_cfb128| aes_cbc256 | aes_ige256 | rc2_cbc | aes_ctr| rc4 </code> </p>
+ blowfish_cbc | blowfish_cfb64 | aes_cbc128 | aes_cfb8 | aes_cfb128| aes_cbc256 | aes_ige256 | rc2_cbc | aes_ctr| rc4 </code> </p>
<p><code> public_key_algorithms() = rsa |dss | ecdsa | dh | ecdh | ec_gf2m</code>
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/crypto_app.xml b/lib/crypto/doc/src/crypto_app.xml
index 6d26076c04..1d10773401 100644
--- a/lib/crypto/doc/src/crypto_app.xml
+++ b/lib/crypto/doc/src/crypto_app.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE appref SYSTEM "appref.dtd">
<appref>
<header>
<copyright>
<year>1999</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 53249479f1..34f2e3c469 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -30,6 +30,110 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix memory leaks and invalid deallocations in
+ <c>mod_pow</c>, <c>mod_exp</c> and
+ <c>generate_key(srp,...)</c> when bad arguments are
+ passed. (Thanks to Florian Zumbiehi)</p>
+ <p>
+ Own Id: OTP-11550</p>
+ </item>
+ <item>
+ <p>
+ Correction of the word 'ChipherText' throughout the
+ documentation (Thanks to Andrew Tunnell-Jones)</p>
+ <p>
+ Own Id: OTP-11609</p>
+ </item>
+ <item>
+ <p>
+ Fix fatal bug when using a hmac context variable in more
+ than one call to <c>hmac_update</c> or <c>hmac_final</c>.
+ The reuse of hmac contexts has never worked as the
+ underlying OpenSSL implementation does not support it. It
+ is now documented as having undefined behaviour, but it
+ does not crash or corrupt the VM anymore.</p>
+ <p>
+ Own Id: OTP-11724</p>
+ </item>
+ <item>
+ <p>
+ Crypto handles out-of-memory with a controlled abort
+ instead of crash/corruption. (Thanks to Florian Zumbiehi)</p>
+ <p>
+ Own Id: OTP-11725</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ By giving --enable-static-{nifs,drivers} to configure it
+ is now possible to statically linking of nifs and drivers
+ to the main Erlang VM binary. At the moment only the asn1
+ and crypto nifs of the Erlang/OTP nifs and drivers have
+ been prepared to be statically linked. For more details
+ see the Installation Guide in the System documentation.</p>
+ <p>
+ Own Id: OTP-11258</p>
+ </item>
+ <item>
+ <p>
+ Add IGE mode for AES cipher in crypto (Thanks to Yura
+ Beznos).</p>
+ <p>
+ Own Id: OTP-11522</p>
+ </item>
+ <item>
+ <p>
+ Moved elliptic curve definition from the crypto
+ NIF/OpenSSL into Erlang code, adds the RFC-5639 brainpool
+ curves and makes TLS use them (RFC-7027).</p>
+ <p>
+ Thanks to Andreas Schultz</p>
+ <p>
+ Own Id: OTP-11578</p>
+ </item>
+ <item>
+ <p>
+ Remove all obsolete application processes from crypto and
+ make it into a pure library application.</p>
+ <p>
+ Own Id: OTP-11619</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 3.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/crypto/src/crypto.app.src b/lib/crypto/src/crypto.app.src
index d3084ff336..823a27ee39 100644
--- a/lib/crypto/src/crypto.app.src
+++ b/lib/crypto/src/crypto.app.src
@@ -23,6 +23,7 @@
crypto_ec_curves]},
{registered, []},
{applications, [kernel, stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["erts-6.0","stdlib-2.0","kernel-3.0"]}]}.
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 5bf52fc8a4..e1fbbf9ab8 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -210,7 +210,7 @@ supports()->
[{hashs, Hashs},
{ciphers, [des_cbc, des_cfb, des3_cbc, des_ede3, blowfish_cbc,
- blowfish_cfb64, blowfish_ofb64, blowfish_ecb, aes_cbc128, aes_cfb128,
+ blowfish_cfb64, blowfish_ofb64, blowfish_ecb, aes_cbc128, aes_cfb8, aes_cfb128,
aes_cbc256, rc2_cbc, aes_ctr, rc4] ++ Ciphers},
{public_keys, [rsa, dss, dh, srp] ++ PubKeys}
].
@@ -281,7 +281,7 @@ hmac_final_n(_Context, _HashLen) -> ? nif_stub.
%% Ecrypt/decrypt %%%
-spec block_encrypt(des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 | blowfish_cbc |
- blowfish_cfb64 | aes_cbc128 | aes_cfb128 | aes_cbc256 | rc2_cbc,
+ blowfish_cfb64 | aes_cbc128 | aes_cfb8 | aes_cfb128 | aes_cbc256 | rc2_cbc,
Key::iodata(), Ivec::binary(), Data::iodata()) -> binary().
block_encrypt(des_cbc, Key, Ivec, Data) ->
@@ -306,6 +306,8 @@ block_encrypt(aes_cbc256, Key, Ivec, Data) ->
aes_cbc_256_encrypt(Key, Ivec, Data);
block_encrypt(aes_ige256, Key, Ivec, Data) ->
aes_ige_256_encrypt(Key, Ivec, Data);
+block_encrypt(aes_cfb8, Key, Ivec, Data) ->
+ aes_cfb_8_encrypt(Key, Ivec, Data);
block_encrypt(aes_cfb128, Key, Ivec, Data) ->
aes_cfb_128_encrypt(Key, Ivec, Data);
block_encrypt(rc2_cbc, Key, Ivec, Data) ->
@@ -313,7 +315,7 @@ block_encrypt(rc2_cbc, Key, Ivec, Data) ->
-spec block_decrypt(des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 | blowfish_cbc |
blowfish_cfb64 | blowfish_ofb64 | aes_cbc128 | aes_cbc256 | aes_ige256 |
- aes_cfb128 | rc2_cbc,
+ aes_cfb8 | aes_cfb128 | rc2_cbc,
Key::iodata(), Ivec::binary(), Data::iodata()) -> binary().
block_decrypt(des_cbc, Key, Ivec, Data) ->
@@ -338,6 +340,8 @@ block_decrypt(aes_cbc256, Key, Ivec, Data) ->
aes_cbc_256_decrypt(Key, Ivec, Data);
block_decrypt(aes_ige256, Key, Ivec, Data) ->
aes_ige_256_decrypt(Key, Ivec, Data);
+block_decrypt(aes_cfb8, Key, Ivec, Data) ->
+ aes_cfb_8_decrypt(Key, Ivec, Data);
block_decrypt(aes_cfb128, Key, Ivec, Data) ->
aes_cfb_128_decrypt(Key, Ivec, Data);
block_decrypt(rc2_cbc, Key, Ivec, Data) ->
@@ -1159,7 +1163,21 @@ blowfish_ofb64_encrypt(_Key, _IVec, _Data) -> ?nif_stub.
%%
-%% AES in cipher feedback mode (CFB)
+%% AES in cipher feedback mode (CFB) - 8 bit shift
+%%
+-spec aes_cfb_8_encrypt(iodata(), binary(), iodata()) -> binary().
+-spec aes_cfb_8_decrypt(iodata(), binary(), iodata()) -> binary().
+
+aes_cfb_8_encrypt(Key, IVec, Data) ->
+ aes_cfb_8_crypt(Key, IVec, Data, true).
+
+aes_cfb_8_decrypt(Key, IVec, Data) ->
+ aes_cfb_8_crypt(Key, IVec, Data, false).
+
+aes_cfb_8_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
+
+%%
+%% AES in cipher feedback mode (CFB) - 128 bit shift
%%
-spec aes_cfb_128_encrypt(iodata(), binary(), iodata()) -> binary().
-spec aes_cfb_128_decrypt(iodata(), binary(), iodata()) -> binary().
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 63552d2e70..479c947029 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -55,6 +55,7 @@ all() ->
{group, blowfish_cfb64},
{group, blowfish_ofb64},
{group, aes_cbc128},
+ {group, aes_cfb8},
{group, aes_cfb128},
{group, aes_cbc256},
{group, aes_ige256},
@@ -90,6 +91,7 @@ groups() ->
{des3_cbf,[], [block]},
{rc2_cbc,[], [block]},
{aes_cbc128,[], [block]},
+ {aes_cfb8,[], [block]},
{aes_cfb128,[], [block]},
{aes_cbc256,[], [block]},
{aes_ige256,[], [block]},
@@ -723,6 +725,9 @@ group_config(aes_cbc256, Config) ->
group_config(aes_ige256, Config) ->
Block = aes_ige256(),
[{block, Block} | Config];
+group_config(aes_cfb8, Config) ->
+ Block = aes_cfb8(),
+ [{block, Block} | Config];
group_config(aes_cfb128, Config) ->
Block = aes_cfb128(),
[{block, Block} | Config];
@@ -1164,6 +1169,25 @@ aes_ige256() ->
hexstr2bin("f69f2445df4f9b17ad2b417be66c3710")}
].
+aes_cfb8() ->
+ [{aes_cfb8,
+ hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"),
+ hexstr2bin("000102030405060708090a0b0c0d0e0f"),
+ hexstr2bin("6bc1bee22e409f96e93d7e117393172a")},
+ {aes_cfb8,
+ hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"),
+ hexstr2bin("3B3FD92EB72DAD20333449F8E83CFB4A"),
+ hexstr2bin("ae2d8a571e03ac9c9eb76fac45af8e51")},
+ {aes_cfb8,
+ hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"),
+ hexstr2bin("C8A64537A0B3A93FCDE3CDAD9F1CE58B"),
+ hexstr2bin("30c81c46a35ce411e5fbc1191a0a52ef")},
+ {aes_cfb8,
+ hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"),
+ hexstr2bin("26751F67A3CBB140B1808CF187A4F4DF"),
+ hexstr2bin("f69f2445df4f9b17ad2b417be66c3710")}
+ ].
+
aes_cfb128() ->
[{aes_cfb128,
hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"),
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 98c071cf87..a2bd6f851a 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 3.2
+CRYPTO_VSN = 3.3
diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml
index a3543a1e11..8832f99fc3 100644
--- a/lib/debugger/doc/src/notes.xml
+++ b/lib/debugger/doc/src/notes.xml
@@ -32,6 +32,72 @@
<p>This document describes the changes made to the Debugger
application.</p>
+<section><title>Debugger 4.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The debugger now correctly evaluates code such as '<c>X =
+ true andalso X</c>'. (Thanks to Anthony Ramine.)</p>
+ <p>
+ Own Id: OTP-11553</p>
+ </item>
+ <item>
+ <p>
+ A few subtle bugs in the evaluation of code in the
+ debugger has been corrected. (Thanks to Anthony Ramine.)</p>
+ <p>
+ Own Id: OTP-11676</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Removed gs based applications and gs based backends. The
+ <c>observer</c> application replaces the removed
+ applications.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10915</p>
+ </item>
+ <item>
+ <p>
+ Support Maps syntax in debugger (Thanks to Anthony
+ Ramine).</p>
+ <p>
+ Own Id: OTP-11673</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Debugger 3.2.12</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index 1d36aae8ee..77297de0f3 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -663,12 +663,16 @@ expr({map,Line,Fs0}, Bs0, Ieval) ->
expr({map,Line,E0,Fs0}, Bs0, Ieval0) ->
Ieval = Ieval0#ieval{line=Line,top=false},
{value,E,Bs1} = expr(E0, Bs0, Ieval),
- {Fs,Bs2} = eval_map_fields(Fs0, Bs1, Ieval),
- Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi);
- ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi) end,
- E, Fs),
- {value,Value,Bs2};
-
+ case E of
+ #{} ->
+ {Fs,Bs2} = eval_map_fields(Fs0, Bs0, Ieval),
+ Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi);
+ ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi)
+ end, E, Fs),
+ {value,Value,merge_bindings(Bs2, Bs1, Ieval)};
+ _ ->
+ exception(error, {badarg,E}, Bs1, Ieval)
+ end;
%% A block of statements
expr({block,Line,Es},Bs,Ieval) ->
seq(Es, Bs, Ieval#ieval{line=Line});
diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl
index 266cf239dd..ad05a7c529 100644
--- a/lib/debugger/src/dbg_iload.erl
+++ b/lib/debugger/src/dbg_iload.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -42,18 +42,21 @@
load_mod(Mod, File, Binary, Db) ->
Flag = process_flag(trap_exit, true),
- Pid = spawn_link(fun () -> load_mod1(Mod, File, Binary, Db) end),
+ Pid = spawn_link(load_mod1(Mod, File, Binary, Db)),
receive
{'EXIT', Pid, What} ->
process_flag(trap_exit, Flag),
What
end.
--spec load_mod1(atom(), file:filename(), binary(), ets:tid()) -> no_return().
+-spec load_mod1(atom(), file:filename(), binary(), ets:tid()) ->
+ fun(() -> no_return()).
load_mod1(Mod, File, Binary, Db) ->
- store_module(Mod, File, Binary, Db),
- exit({ok, Mod}).
+ fun() ->
+ store_module(Mod, File, Binary, Db),
+ exit({ok, Mod})
+ end.
%%====================================================================
%% Internal functions
diff --git a/lib/debugger/src/debugger.app.src b/lib/debugger/src/debugger.app.src
index 84fb98c94e..f102385d39 100644
--- a/lib/debugger/src/debugger.app.src
+++ b/lib/debugger/src/debugger.app.src
@@ -46,4 +46,6 @@
int
]},
{registered, [dbg_iserver, dbg_wx_mon, dbg_wx_winman]},
- {applications, [kernel, stdlib]}]}.
+ {applications, [kernel, stdlib]},
+ {runtime_dependencies, ["wx-1.2","stdlib-2.0","kernel-3.0","erts-6.0",
+ "compiler-5.0"]}]}.
diff --git a/lib/debugger/src/debugger.erl b/lib/debugger/src/debugger.erl
index 8a2ac28df5..77fd0acb70 100644
--- a/lib/debugger/src/debugger.erl
+++ b/lib/debugger/src/debugger.erl
@@ -51,12 +51,6 @@
%% ------------------------------
%% Help window for creating new breakpoints.
%%
-%% dbg_wx_edit, dbg_wx_edit_win
-%% --------------------------------------
-%% Help window for editing terms, used for setting backtrace size
-%% (i.e. how many stack frames to display in the attach process window)
-%% and changing variable values.
-%%
%% dbg_wx_interpret, dbg_wx_filedialog_win
%% --------------------------------------
%% Help window for selecting modules to interpret.
diff --git a/lib/debugger/test/int_eval_SUITE.erl b/lib/debugger/test/int_eval_SUITE.erl
index 4ffcf7888e..9d7ef238e3 100644
--- a/lib/debugger/test/int_eval_SUITE.erl
+++ b/lib/debugger/test/int_eval_SUITE.erl
@@ -28,7 +28,7 @@
bifs_outside_erlang/1, spawning/1, applying/1,
catch_and_throw/1, external_call/1, test_module_info/1,
apply_interpreted_fun/1, apply_uninterpreted_fun/1,
- interpreted_exit/1, otp_8310/1, stacktrace/1]).
+ interpreted_exit/1, otp_8310/1, stacktrace/1, maps/1]).
%% Helpers.
-export([applier/3]).
@@ -44,7 +44,7 @@ all() ->
[bifs_outside_erlang, spawning, applying,
catch_and_throw, external_call, test_module_info,
apply_interpreted_fun, apply_uninterpreted_fun,
- interpreted_exit, otp_8310, stacktrace].
+ interpreted_exit, otp_8310, stacktrace, maps].
groups() ->
[].
@@ -291,6 +291,12 @@ stacktrace(Config) when is_list(Config) ->
end,
ok.
+maps(Config) when is_list(Config) ->
+ Fun = fun () -> ?IM:empty_map_update([camembert]) end,
+ {'EXIT',{{badarg,[camembert]},_}} = spawn_eval(Fun),
+ [#{hello := 0, price := 0}] = spawn_eval(fun () -> ?IM:update_in_fun() end),
+ ok.
+
do_eval(Config, Mod) ->
?line DataDir = ?config(data_dir, Config),
diff --git a/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
index ab485fd350..7f55360f48 100644
--- a/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
+++ b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
@@ -29,6 +29,7 @@
-export([more_catch/1,more_nocatch/1,exit_me/0]).
-export([f/1, f_try/1, f_catch/1]).
-export([otp_5837/1, otp_8310/0]).
+-export([empty_map_update/1, update_in_fun/0]).
%% Internal exports.
-export([echo/2,my_subtract/2,catch_a_ball/0,throw_a_ball/0]).
@@ -241,3 +242,8 @@ otp_8310() ->
true = begin (X3 = true) orelse X3, X3 end,
false = begin (X4 = false) orelse X4, X4 end,
ok.
+
+empty_map_update(Map) -> Map#{}.
+
+update_in_fun() ->
+ lists:map(fun (X) -> X#{price := 0} end, [#{hello => 0, price => nil}]).
diff --git a/lib/debugger/test/map_SUITE.erl b/lib/debugger/test/map_SUITE.erl
index e9f4ea1fad..0076193725 100644
--- a/lib/debugger/test/map_SUITE.erl
+++ b/lib/debugger/test/map_SUITE.erl
@@ -226,8 +226,8 @@ t_update_map_expressions(Config) when is_list(Config) ->
#{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 },
%% Error cases, FIXME: should be 'badmap'?
- {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }),
- {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }),
+ {'EXIT',{{badarg,<<>>},_}} = (catch (id(<<>>))#{ a := 42, b => 2 }),
+ {'EXIT',{{badarg,[]},_}} = (catch (id([]))#{ a := 42, b => 2 }),
ok.
@@ -244,7 +244,7 @@ t_update_assoc(Config) when is_list(Config) ->
%% Errors cases.
BadMap = id(badmap),
- {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}),
+ {'EXIT',{{badarg,BadMap},_}} = (catch BadMap#{nonexisting=>val}),
ok.
@@ -790,16 +790,16 @@ t_map_encode_decode(Config) when is_list(Config) ->
%% literally #{ b=>2, a=>1 } in the internal order
#{ a:=1, b:=2 } =
- erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,100,0,1,97,97,2,97,1>>),
+ erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,97,2,100,0,1,97,97,1>>),
%% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order
#{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3,
107,0,2,104,105, % "hi" :: list()
- 100,0,1,97, % a :: atom()
- 100,0,1,98, % b :: atom()
107,0,5,118,97,108,117,101, % "value" :: list()
+ 100,0,1,97, % a :: atom()
97,33, % 33 :: integer()
+ 100,0,1,98, % b :: atom()
97,55 % 55 :: integer()
>>),
@@ -829,7 +829,8 @@ map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) ->
B0 = erlang:term_to_binary(M1),
Ls = lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, [{K, erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs]),
%% sort Ks and Vs according to term spec, then match it
- ok = match_encoded_map(B0, length(Ls), [Kbin||{_,Kbin,_}<-Ls] ++ [Vbin||{_,_,Vbin}<-Ls]),
+ KVbins = lists:foldr(fun({_,Kbin,Vbin}, Acc) -> [Kbin,Vbin | Acc] end, [], Ls),
+ ok = match_encoded_map(B0, length(Ls), KVbins),
%% decode and match it
M1 = erlang:binary_to_term(B0),
map_encode_decode_and_match(Pairs,Ls,M1);
diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk
index a245e26a55..cd107599e9 100644
--- a/lib/debugger/vsn.mk
+++ b/lib/debugger/vsn.mk
@@ -1 +1 @@
-DEBUGGER_VSN = 3.2.12
+DEBUGGER_VSN = 4.0
diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index a92b890a80..3de60b2f7a 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>2013</year>
+ <year>2006</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -50,33 +50,31 @@
<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"><![CDATA[
- dialyzer --help
- ]]></code>
+ <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>
- <code type="none"><![CDATA[
+ <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>
+ 2 - No problems were encountered, but warnings were emitted.</code>
<p>Usage:</p>
- <code type="none"><![CDATA[
+ <code type="none">
dialyzer [--help] [--version] [--shell] [--quiet] [--verbose]
[-pa dir]* [--plt plt] [--plts plt*] [-Ddefine]*
- [-I include_dir]* [--output_plt file] [-Wwarn]*
+ [-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]
- [--no_native] [--fullpath]
- ]]></code>
+ [--dump_callgraph file] [--no_native] [--fullpath]
+ [--statistics]</code>
<p>Options:</p>
<taglist>
<tag><c><![CDATA[files_or_dirs]]></c> (for backwards compatibility also
- as: <c><![CDATA[-c files_or_dirs]]></c></tag>
+ 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
@@ -88,16 +86,14 @@
analysis.</item>
<tag><c><![CDATA[--apps applications]]></c></tag>
<item>Option typically used when building or modifying a plt as in:
- <code type="none"><![CDATA[
- dialyzer --build_plt --apps erts kernel stdlib mnesia ...
- ]]></code>
+ <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:
- <code type="none"><![CDATA[
- dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam
- ]]></code></item>
+ <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
@@ -129,24 +125,26 @@
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:
- <code type="none"><![CDATA[
+ <code type="none">
dialyzer --build_plt --output_plt plt_1 files_to_include
...
- dialyzer --build_plt --output_plt plt_n files_to_include
- ]]></code>
+ dialyzer --build_plt --output_plt plt_n files_to_include</code>
and then can be used in either of the following ways:
- <code type="none"><![CDATA[
- dialyzer files_to_analyze --plts plt_1 ... plt_n
- ]]></code>
+ <code type="none">
+ dialyzer files_to_analyze --plts plt_1 ... plt_n</code>
or:
- <code type="none"><![CDATA[
- dialyzer --plts plt_1 ... plt_n -- files_to_analyze
- ]]></code>
+ <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>).</item>
+ <c><![CDATA[dialyzer -Whelp]]></c>).
+ Note that the options can also be given in the file with a
+ <c>-dialyzer({nowarn_tag, WarningTags})</c> attribute.
+ See <seealso
+ marker="doc/reference_manual:typespec#suppression">Erlang Reference
+ Manual</seealso> 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>
@@ -220,8 +218,6 @@
<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_tuple_as_fun]]></c></tag>
- <item>Suppress warnings for using tuples instead of funs.</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>
@@ -229,9 +225,16 @@
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_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
@@ -278,13 +281,13 @@
</type>
<desc>
<p>Dialyzer GUI version.</p>
- <code type="none"><![CDATA[
+ <code type="none">
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
+ | {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()}
@@ -304,14 +307,15 @@ WarnOpts :: no_return
| no_match
| no_opaque
| no_fail_call
+ | no_contracts
+ | no_behaviours
+ | no_undefined_callbacks
+ | unmatched_returns
| error_handling
| race_conditions
- | behaviours
- | unmatched_returns
| overspecs
| underspecs
- | specdiffs
- ]]></code>
+ | specdiffs</code>
</desc>
</func>
<func>
@@ -323,17 +327,30 @@ WarnOpts :: no_return
</type>
<desc>
<p>Dialyzer command line version.</p>
- <code type="none"><![CDATA[
+ <code type="none">
Warnings :: [{Tag, Id, Msg}]
-Tag :: 'warn_return_no_exit' | 'warn_return_only_exit'
- | 'warn_not_called' | 'warn_non_proper_list'
- | 'warn_fun_app' | 'warn_matching'
- | 'warn_failing_call' | 'warn_contract_types'
- | 'warn_contract_syntax' | 'warn_contract_not_equal'
- | 'warn_contract_subtype' | 'warn_contract_supertype'
+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'
Id = {File :: string(), Line :: integer()}
-Msg = msg() -- Undefined
-]]></code>
+Msg = msg() -- Undefined</code>
</desc>
</func>
<func>
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index b61b1af1b0..05baa93557 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -31,6 +31,173 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 2.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Dialyzer will no longer emit warnings when inspecting
+ 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.
+ </p>
+ <p>
+ Own Id: OTP-10397</p>
+ </item>
+ <item>
+ <p>
+ With <c>--Wunmatched_returns</c>, dialyzer will no longer
+ warn when the value of a list comprehension is ignored,
+ provided that the each value in the list would be an
+ atomic value (such as integer or atoms, as opposed to
+ tuples and lists). Example: ignoring '<c>[io:format(...)
+ || ...]</c>' will not cause a warning, while ignoring
+ '<c>[file:close(Fd) || ...]</c>' will.</p>
+ <p>
+ Own Id: OTP-11626</p>
+ </item>
+ <item>
+ <p>
+ The man page for dialyzer now contains correct
+ information regarding -Wno_behaviours. (Thanks to Steve
+ Vinosky.)</p>
+ <p>
+ Own Id: OTP-11706</p>
+ </item>
+ <item>
+ <p> Fix handling of 'on_load' attribute. (Thanks to
+ Kostis Sagonas.) </p>
+ <p>
+ Own Id: OTP-11743</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ <item>
+ <p> The generalization of guard constraints has been
+ modified. </p>
+ <p>
+ Own Id: OTP-11798 Aux Id: seq12547 </p>
+ </item>
+ <item>
+ <p> Dialyzer now plays nicely with funs that come as
+ "external" arguments. (Thanks to Stavros Aronis for
+ fixing the bug.) </p>
+ <p>
+ Own Id: OTP-11826</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The pre-defined types <c>array/0</c>, <c>dict/0</c>,
+ <c>digraph/0</c>, <c>gb_set/0</c>, <c>gb_tree/0</c>,
+ <c>queue/0</c>, <c>set/0</c>, and <c>tid/0</c> have been
+ deprecated. They will be removed in Erlang/OTP 18.0. </p>
+ <p> Instead the types <c>array:array/0</c>,
+ <c>dict:dict/0</c>, <c>digraph:graph/0</c>,
+ <c>gb_set:set/0</c>, <c>gb_tree:tree/0</c>,
+ <c>queue:queue/0</c>, <c>sets:set/0</c>, and
+ <c>ets:tid/0</c> can be used. (Note: it has always been
+ necessary to use <c>ets:tid/0</c>.) </p> <p> It is
+ allowed in Erlang/OTP 17.0 to locally re-define the types
+ <c>array/0</c>, <c>dict/0</c>, and so on. </p> <p> New
+ types <c>array:array/1</c>, <c>dict:dict/2</c>,
+ <c>gb_sets:set/1</c>, <c>gb_trees:tree/2</c>,
+ <c>queue:queue/1</c>, and <c>sets:set/1</c> have been
+ added. </p> <p> A compiler option,
+ <c>nowarn_deprecated_type</c>, has been introduced. By
+ including the attribute </p> <c>
+ -compile(nowarn_deprecated_type).</c> <p> in an Erlang
+ source file, warnings about deprecated types can be
+ avoided in Erlang/OTP 17.0. </p> <p> The option can also
+ be given as a compiler flag: </p> <c> erlc
+ +nowarn_deprecated_type file.erl</c>
+ <p>
+ Own Id: OTP-10342</p>
+ </item>
+ <item>
+ <p>
+ Removed gs based applications and gs based backends. The
+ <c>observer</c> application replaces the removed
+ applications.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10915</p>
+ </item>
+ <item>
+ <p>
+ Forbid unsized fields in patterns of binary generators
+ and simplified v3_core's translation of bit string
+ generators. (Thanks to Anthony Ramine.)</p>
+ <p>
+ Own Id: OTP-11186</p>
+ </item>
+ <item>
+ <p>
+ EEP43: New data type - Maps</p>
+ <p>
+ With Maps you may for instance: <taglist> <item><c>M0 =
+ #{ a =&gt; 1, b =&gt; 2}, % create
+ associations</c></item> <item><c>M1 = M0#{ a := 10 }, %
+ update values</c></item> <item><c>M2 = M1#{ "hi" =&gt;
+ "hello"}, % add new associations</c></item> <item><c>#{
+ "hi" := V1, a := V2, b := V3} = M2. % match keys with
+ values</c></item> </taglist></p>
+ <p>
+ For information on how to use Maps please see the
+ <seealso marker="doc/reference_manual:maps">Reference
+ Manual</seealso>.</p>
+ <p>
+ The current implementation is without the following
+ features: <taglist> <item>No variable keys</item>
+ <item>No single value access</item> <item>No map
+ comprehensions</item> </taglist></p>
+ <p>
+ Note that Maps is <em>experimental</em> during OTP 17.0.</p>
+ <p>
+ Own Id: OTP-11616</p>
+ </item>
+ <item>
+ <p> Parameterized opaque types have been introduced. </p>
+ <p>
+ Own Id: OTP-11625</p>
+ </item>
+ <item>
+ <p>
+ Some function specs are corrected or moved and some edoc
+ comments are corrected in order to allow use of edoc.
+ (Thanks to Pierre Fenoll)</p>
+ <p>
+ Own Id: OTP-11702</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 2.6.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile
index d7265ba31a..91fbdca5bd 100644
--- a/lib/dialyzer/src/Makefile
+++ b/lib/dialyzer/src/Makefile
@@ -88,7 +88,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
ifeq ($(NATIVE_LIBS_ENABLED),yes)
ERL_COMPILE_FLAGS += +native
endif
-ERL_COMPILE_FLAGS += +warn_exported_vars +warn_unused_import +warn_untyped_record +warn_missing_spec +warnings_as_errors
+ERL_COMPILE_FLAGS += +warn_export_vars +warn_unused_import +warn_untyped_record +warn_missing_spec +warnings_as_errors
# ----------------------------------------------------
# Targets
diff --git a/lib/dialyzer/src/dialyzer.app.src b/lib/dialyzer/src/dialyzer.app.src
index 0d048b607e..1756800c4f 100644
--- a/lib/dialyzer/src/dialyzer.app.src
+++ b/lib/dialyzer/src/dialyzer.app.src
@@ -44,4 +44,7 @@
dialyzer_worker]},
{registered, []},
{applications, [compiler, gs, hipe, kernel, stdlib, wx]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["wx-1.2","syntax_tools-1.6.14","stdlib-2.0",
+ "kernel-3.0","hipe-3.10.3","erts-6.0",
+ "compiler-5.0"]}]}.
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index bb7e39dfda..cec94a49fd 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -172,7 +172,7 @@ run(Opts) ->
end,
case dialyzer_cl:start(OptsRecord) of
{?RET_DISCREPANCIES, Warnings} -> Warnings;
- {?RET_NOTHING_SUSPICIOUS, []} -> []
+ {?RET_NOTHING_SUSPICIOUS, _} -> []
end
catch
throw:{dialyzer_error, ErrorMsg} ->
@@ -447,7 +447,6 @@ message_to_string({opaque_size, [SizeType, Size]}) ->
message_to_string({opaque_call, [M, F, Args, Culprit, OpaqueType]}) ->
io_lib:format("The call ~s:~s~s breaks the opaqueness of the term ~s :: ~s\n",
[M, F, Args, Culprit, OpaqueType]);
-
%%----- Warnings for concurrency errors --------------------
message_to_string({race_condition, [M, F, Args, Reason]}) ->
io_lib:format("The call ~w:~w~s ~s\n", [M, F, Args, Reason]);
@@ -474,7 +473,14 @@ message_to_string({callback_missing, [B, F, A]}) ->
io_lib:format("Undefined callback function ~w/~w (behaviour '~w')\n",
[F, A, B]);
message_to_string({callback_info_missing, [B]}) ->
- io_lib:format("Callback info about the ~w behaviour is not available\n", [B]).
+ io_lib:format("Callback info about the ~w behaviour is not available\n", [B]);
+%%----- Warnings for unknown functions, types, and behaviours -------------
+message_to_string({unknown_type, {M, F, A}}) ->
+ io_lib:format("Unknown type ~w:~w/~w", [M, F, A]);
+message_to_string({unknown_function, {M, F, A}}) ->
+ io_lib:format("Unknown function ~w:~w/~w", [M, F, A]);
+message_to_string({unknown_behaviour, B}) ->
+ io_lib:format("Unknown behaviour ~w", [B]).
%%-----------------------------------------------------------------------------
%% Auxiliary functions below
@@ -557,4 +563,4 @@ form_position_string(ArgNs) ->
ordinal(1) -> "1st";
ordinal(2) -> "2nd";
ordinal(3) -> "3rd";
-ordinal(N) when is_integer(N) -> io_lib:format("~wth",[N]).
+ordinal(N) when is_integer(N) -> io_lib:format("~wth", [N]).
diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl
index 105a174e31..9a25f86512 100644
--- a/lib/dialyzer/src/dialyzer.hrl
+++ b/lib/dialyzer/src/dialyzer.hrl
@@ -2,7 +2,7 @@
%%%
%%% %CopyrightBegin%
%%%
-%%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%%% Copyright Ericsson AB 2006-2014. All Rights Reserved.
%%%
%%% The contents of this file are subject to the Erlang Public License,
%%% Version 1.1, (the "License"); you may not use this file except in
@@ -58,6 +58,7 @@
-define(WARN_RACE_CONDITION, warn_race_condition).
-define(WARN_BEHAVIOUR, warn_behaviour).
-define(WARN_UNDEFINED_CALLBACK, warn_undefined_callbacks).
+-define(WARN_UNKNOWN, warn_unknown).
%%
%% The following type has double role:
@@ -73,7 +74,7 @@
| ?WARN_CONTRACT_SUPERTYPE | ?WARN_CALLGRAPH
| ?WARN_UNMATCHED_RETURN | ?WARN_RACE_CONDITION
| ?WARN_BEHAVIOUR | ?WARN_CONTRACT_RANGE
- | ?WARN_UNDEFINED_CALLBACK.
+ | ?WARN_UNDEFINED_CALLBACK | ?WARN_UNKNOWN.
%%
%% This is the representation of each warning as they will be returned
@@ -88,12 +89,6 @@
-type dial_error() :: any(). %% XXX: underspecified
%%--------------------------------------------------------------------
-%% THIS TYPE SHOULD ONE DAY DISAPPEAR -- IT DOES NOT BELONG HERE
-%%--------------------------------------------------------------------
-
--type ordset(T) :: [T] . %% XXX: temporarily
-
-%%--------------------------------------------------------------------
%% Basic types used either in the record definitions below or in other
%% parts of the application
%%--------------------------------------------------------------------
@@ -143,7 +138,7 @@
init_plts = [] :: [file:filename()],
include_dirs = [] :: [file:filename()],
output_plt = none :: 'none' | file:filename(),
- legal_warnings = ordsets:new() :: ordset(dial_warn_tag()),
+ legal_warnings = ordsets:new() :: ordsets:ordset(dial_warn_tag()),
report_mode = normal :: rep_mode(),
erlang_mode = false :: boolean(),
use_contracts = true :: boolean(),
@@ -167,4 +162,4 @@
dialyzer_timing:end_stamp(Server),
Var
end).
--define(timing(Server, Msg, Expr),?timing(Server, Msg, _T, Expr)).
+-define(timing(Server, Msg, Expr), ?timing(Server, Msg, _T, Expr)).
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index 2a633c5e37..6a33a2acb3 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -249,7 +249,7 @@ compile_and_store(Files, #analysis_state{codeserver = CServer,
timing_server = Timing,
parent = Parent} = State) ->
send_log(Parent, "Reading files and computing callgraph... "),
- {T1, _} = statistics(runtime),
+ {T1, _} = statistics(wall_clock),
Callgraph = dialyzer_callgraph:new(),
CompileInit = make_compile_init(State, Callgraph),
{{Failed, NoWarn, Modules}, NextLabel} =
@@ -272,13 +272,13 @@ compile_and_store(Files, #analysis_state{codeserver = CServer,
[[Reason || {_Filename, Reason} <- Failed]]),
exit({error, Msg})
end,
- {T2, _} = statistics(runtime),
+ {T2, _} = statistics(wall_clock),
Msg1 = io_lib:format("done in ~.2f secs\nRemoving edges... ", [(T2-T1)/1000]),
send_log(Parent, Msg1),
Callgraph =
?timing(Timing, "clean", _C2,
cleanup_callgraph(State, CServer2, Callgraph, Modules)),
- {T3, _} = statistics(runtime),
+ {T3, _} = statistics(wall_clock),
Msg2 = io_lib:format("done in ~.2f secs\n", [(T3-T2)/1000]),
send_log(Parent, Msg2),
{Callgraph, sets:from_list(NoWarn), CServer2}.
@@ -620,9 +620,9 @@ dump_callgraph(CallGraph, State, #analysis{callgraph_file = File} = Analysis) ->
Extension = filename:extension(File),
Start_Msg = io_lib:format("Dumping the callgraph... ", []),
send_log(State#analysis_state.parent, Start_Msg),
- {T1, _} = statistics(runtime),
+ {T1, _} = statistics(wall_clock),
dump_callgraph(CallGraph, State, Analysis, Extension),
- {T2, _} = statistics(runtime),
+ {T2, _} = statistics(wall_clock),
Finish_Msg = io_lib:format("done in ~2f secs\n", [(T2-T1)/1000]),
send_log(State#analysis_state.parent, Finish_Msg),
ok.
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index 3e68d64d53..3e7d9dfa99 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -504,7 +504,7 @@ hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) ->
_ ->
Mods = [lists, dict, digraph, digraph_utils, ets,
gb_sets, gb_trees, ordsets, sets, sofs,
- cerl, cerl_trees, erl_types, erl_bif_types,
+ cerl, erl_types, cerl_trees, erl_bif_types,
dialyzer_analysis_callgraph, dialyzer, dialyzer_behaviours,
dialyzer_codeserver, dialyzer_contracts,
dialyzer_coordinator, dialyzer_dataflow, dialyzer_dep,
@@ -533,7 +533,7 @@ hc(Mod) ->
case code:is_module_native(Mod) of
true -> ok;
false ->
- %% io:format(" ~s", [Mod]),
+ %% io:format(" ~w", [Mod]),
{ok, Mod} = hipe:c(Mod),
ok
end.
@@ -656,7 +656,8 @@ return_value(State = #cl_state{erlang_mode = ErlangMode,
mod_deps = ModDeps,
output_plt = OutputPlt,
plt_info = PltInfo,
- stored_warnings = StoredWarnings},
+ stored_warnings = StoredWarnings,
+ legal_warnings = LegalWarnings},
Plt) ->
case OutputPlt =:= none of
true -> ok;
@@ -676,16 +677,33 @@ return_value(State = #cl_state{erlang_mode = ErlangMode,
maybe_close_output_file(State),
{RetValue, []};
true ->
- {RetValue, process_warnings(StoredWarnings)}
+ Unknown =
+ case ordsets:is_element(?WARN_UNKNOWN, LegalWarnings) of
+ true ->
+ unknown_functions(State) ++
+ unknown_types(State) ++
+ unknown_behaviours(State);
+ false -> []
+ end,
+ UnknownWarnings =
+ [{?WARN_UNKNOWN, {_Filename = "", _Line = 0}, W} || W <- Unknown],
+ AllWarnings =
+ UnknownWarnings ++ process_warnings(StoredWarnings),
+ {RetValue, AllWarnings}
end.
+unknown_functions(#cl_state{external_calls = Calls}) ->
+ [{unknown_function, MFA} || MFA <- Calls].
+
print_ext_calls(#cl_state{report_mode = quiet}) ->
ok;
print_ext_calls(#cl_state{output = Output,
external_calls = Calls,
stored_warnings = Warnings,
- output_format = Format}) ->
- case Calls =:= [] of
+ output_format = Format,
+ legal_warnings = LegalWarnings}) ->
+ case not ordsets:is_element(?WARN_UNKNOWN, LegalWarnings)
+ orelse Calls =:= [] of
true -> ok;
false ->
case Warnings =:= [] of
@@ -708,14 +726,19 @@ do_print_ext_calls(Output, [{M,F,A}|T], Before) ->
do_print_ext_calls(_, [], _) ->
ok.
+unknown_types(#cl_state{external_types = Types}) ->
+ [{unknown_type, MFA} || MFA <- Types].
+
print_ext_types(#cl_state{report_mode = quiet}) ->
ok;
print_ext_types(#cl_state{output = Output,
external_calls = Calls,
external_types = Types,
stored_warnings = Warnings,
- output_format = Format}) ->
- case Types =:= [] of
+ output_format = Format,
+ legal_warnings = LegalWarnings}) ->
+ case not ordsets:is_element(?WARN_UNKNOWN, LegalWarnings)
+ orelse Types =:= [] of
true -> ok;
false ->
case Warnings =:= [] andalso Calls =:= [] of
@@ -738,6 +761,15 @@ do_print_ext_types(Output, [{M,F,A}|T], Before) ->
do_print_ext_types(_, [], _) ->
ok.
+unknown_behaviours(#cl_state{unknown_behaviours = DupBehaviours,
+ legal_warnings = LegalWarnings}) ->
+ case ordsets:is_element(?WARN_BEHAVIOUR, LegalWarnings) of
+ false -> [];
+ true ->
+ Behaviours = lists:usort(DupBehaviours),
+ [{unknown_behaviour, B} || B <- Behaviours]
+ end.
+
%%print_unknown_behaviours(#cl_state{report_mode = quiet}) ->
%% ok;
print_unknown_behaviours(#cl_state{output = Output,
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index db27b2037d..04ce0e8bc3 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-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -357,12 +357,13 @@ help_warnings() ->
help_message() ->
S = "Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose]
[-pa dir]* [--plt plt] [--plts plt*] [-Ddefine]*
- [-I include_dir]* [--output_plt file] [-Wwarn]*
+ [-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]
- [--no_native] [--fullpath] [--statistics]
+ [--dump_callgraph file] [--no_native] [--fullpath]
+ [--statistics]
Options:
files_or_dirs (for backwards compatibility also as: -c files_or_dirs)
Use Dialyzer from the command line to detect defects in the
@@ -495,14 +496,16 @@ warning_options_msg() ->
Suppress warnings for unused functions.
-Wno_improper_lists
Suppress warnings for construction of improper lists.
- -Wno_tuple_as_fun
- Suppress warnings for using tuples instead of funs.
-Wno_fun_app
Suppress warnings for fun applications that will fail.
-Wno_match
Suppress warnings for patterns that are unused or cannot match.
-Wno_opaque
Suppress warnings for violations of opaqueness of data types.
+ -Wno_fail_call
+ Suppress warnings for failing calls.
+ -Wno_contracts
+ Suppress warnings about invalid contracts.
-Wno_behaviours
Suppress warnings about behaviour callbacks which drift from the published
recommended interfaces.
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 46eaeaa303..1d2dfc7b2d 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -20,6 +20,8 @@
-module(dialyzer_contracts).
+-compile(export_all).
+
-export([check_contract/2,
check_contracts/4,
contracts_without_fun/3,
@@ -439,7 +441,8 @@ contract_from_form([], _RecDict, _FileLine, TypeAcc, FormAcc) ->
{lists:reverse(TypeAcc), lists:reverse(FormAcc)}.
process_constraints(Constrs, RecDict, ExpTypes, AllRecords) ->
- Init = initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords),
+ Init0 = initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords),
+ Init = remove_cycles(Init0),
constraints_fixpoint(Init, RecDict, ExpTypes, AllRecords).
initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords) ->
@@ -479,12 +482,9 @@ constraints_fixpoint(OldVarDict, Constrs, RecDict, ExpTypes, AllRecords) ->
constraints_fixpoint(NewVarDict, Constrs, RecDict, ExpTypes, AllRecords)
end.
--define(TYPE_LIMIT, 4).
-
final_form(Form, RecDict, ExpTypes, AllRecords, VarDict) ->
T1 = erl_types:t_from_form(Form, RecDict, VarDict),
- T2 = erl_types:t_solve_remote(T1, ExpTypes, AllRecords),
- erl_types:t_limit(T2, ?TYPE_LIMIT).
+ erl_types:t_solve_remote(T1, ExpTypes, AllRecords).
constraints_to_dict(Constrs, RecDict, ExpTypes, AllRecords, VarDict) ->
Subtypes =
@@ -499,6 +499,74 @@ constraints_to_subs([C|Rest], RecDict, ExpTypes, AllRecords, VarDict, Acc) ->
NewAcc = [{subtype, T1, T2}|Acc],
constraints_to_subs(Rest, RecDict, ExpTypes, AllRecords, VarDict, NewAcc).
+%% Replaces variables with '_' when necessary to break up cycles among
+%% the constraints.
+
+remove_cycles(Constrs0) ->
+ Uses = find_uses(Constrs0),
+ G = digraph:new(),
+ Vs0 = [V || {V, _} <- Uses] ++ [V || {_, V} <- Uses],
+ Vs = lists:usort(Vs0),
+ lists:foreach(fun(V) -> _ = digraph:add_vertex(G, V) end, Vs),
+ lists:foreach(fun({From, To}) ->
+ _ = digraph:add_edge(G, {From, To}, From, To, [])
+ end, Uses),
+ ok = remove_cycles(G, Vs),
+ ToRemove = ordsets:subtract(ordsets:from_list(Uses),
+ ordsets:from_list(digraph:edges(G))),
+ Constrs = remove_uses(ToRemove, Constrs0),
+ digraph:delete(G),
+ Constrs.
+
+find_uses([{Var, Form}|Constrs]) ->
+ UsedVars = form_vars(Form, []),
+ VarName = erl_types:t_var_name(Var),
+ [{VarName, UsedVar} || UsedVar <- UsedVars] ++ find_uses(Constrs);
+find_uses([]) ->
+ [].
+
+form_vars({var, _, '_'}, Vs) -> Vs;
+form_vars({var, _, V}, Vs) -> [V|Vs];
+form_vars(T, Vs) when is_tuple(T) ->
+ form_vars(tuple_to_list(T), Vs);
+form_vars([E|Es], Vs) ->
+ form_vars(Es, form_vars(E, Vs));
+form_vars(_, Vs) -> Vs.
+
+remove_cycles(G, Vs) ->
+ NumberOfEdges = digraph:no_edges(G),
+ lists:foreach(fun(V) ->
+ case digraph:get_cycle(G, V) of
+ false -> true;
+ [V] -> digraph:del_edge(G, {V, V});
+ [V, V1|_] -> digraph:del_edge(G, {V, V1})
+ end
+ end, Vs),
+ case digraph:no_edges(G) =:= NumberOfEdges of
+ true -> ok;
+ false -> remove_cycles(G, Vs)
+ end.
+
+remove_uses([], Constrs) -> Constrs;
+remove_uses([{Var, Use}|ToRemove], Constrs0) ->
+ Constrs = remove_uses(Var, Use, Constrs0),
+ remove_uses(ToRemove, Constrs).
+
+remove_uses(_Var, _Use, []) -> [];
+remove_uses(Var, Use, [Constr|Constrs]) ->
+ {V, Form} = Constr,
+ case erl_types:t_var_name(V) =:= Var of
+ true -> [{V, remove_use(Form, Use)}|Constrs];
+ false -> [Constr|remove_uses(Var, Use, Constrs)]
+ end.
+
+remove_use({var, L, V}, V) -> {var, L, '_'};
+remove_use(T, V) when is_tuple(T) ->
+ list_to_tuple(remove_use(tuple_to_list(T), V));
+remove_use([E|Es], V) ->
+ [remove_use(E, V)|remove_use(Es, V)];
+remove_use(T, _V) -> T.
+
%% Gets the most general domain of a list of domains of all
%% the overloaded contracts
@@ -684,14 +752,7 @@ is_remote_types_related(Contract, CSig, Sig, RecDict) ->
t_from_forms_without_remote([{FType, []}], RecDict) ->
Type0 = erl_types:t_from_form(FType, RecDict),
- Map =
- fun(Type) ->
- case erl_types:t_is_remote(Type) of
- true -> erl_types:t_none();
- false -> Type
- end
- end,
- {ok, erl_types:t_map(Map, Type0)};
+ {ok, erl_types:subst_all_remote(Type0, erl_types:t_none())};
t_from_forms_without_remote([{_FType, _Constrs}], _RecDict) ->
%% 'When' constraints
unsupported;
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 692684cd99..e0873b17f8 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -363,20 +363,24 @@ traverse_list([], Map, State, Acc) ->
handle_apply(Tree, Map, State) ->
Args = cerl:apply_args(Tree),
Op = cerl:apply_op(Tree),
- {State1, Map1, ArgTypes} = traverse_list(Args, Map, State),
- {State2, Map2, OpType} = traverse(Op, Map1, State1),
+ {State0, Map1, ArgTypes} = traverse_list(Args, Map, State),
+ {State1, Map2, OpType} = traverse(Op, Map1, State0),
case any_none(ArgTypes) of
true ->
- {State2, Map2, t_none()};
+ {State1, Map2, t_none()};
false ->
- {CallSitesKnown, FunList} =
- case state__lookup_call_site(Tree, State2) of
- error -> {false, []};
- {ok, [external]} -> {false, []};
- {ok, List} -> {true, List}
+ FunList =
+ case state__lookup_call_site(Tree, State) of
+ error -> [external]; %% so that we go directly in the fallback
+ {ok, List} -> List
end,
- case CallSitesKnown of
- false ->
+ FunInfoList = [{local, state__fun_info(Fun, State)} || Fun <- FunList],
+ case
+ handle_apply_or_call(FunInfoList, Args, ArgTypes, Map2, Tree, State1)
+ of
+ {had_external, State2} ->
+ %% Fallback: use whatever info we collected from traversing the op
+ %% instead of the result that has been generalized to t_any().
Arity = length(Args),
OpType1 = t_inf(OpType, t_fun(Arity, t_any())),
case t_is_none(OpType1) of
@@ -408,25 +412,23 @@ handle_apply(Tree, Map, State) ->
{State2, enter_type(Op, OpType1, Map3), Range}
end
end;
- true ->
- FunInfoList = [{local, state__fun_info(Fun, State)}
- || Fun <- FunList],
- handle_apply_or_call(FunInfoList, Args, ArgTypes, Map2, Tree, State1)
+ Normal -> Normal
end
end.
handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State) ->
None = t_none(),
handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State,
- [None || _ <- ArgTypes], None).
+ [None || _ <- ArgTypes], None, false).
handle_apply_or_call([{local, external}|Left], Args, ArgTypes, Map, Tree, State,
- _AccArgTypes, _AccRet) ->
+ _AccArgTypes, _AccRet, _HadExternal) ->
handle_apply_or_call(Left, Args, ArgTypes, Map, Tree, State,
- ArgTypes, t_any());
+ ArgTypes, t_any(), true);
handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
Args, ArgTypes, Map, Tree,
- #state{opaques = Opaques} = State, AccArgTypes, AccRet) ->
+ #state{opaques = Opaques} = State,
+ AccArgTypes, AccRet, HadExternal) ->
Any = t_any(),
AnyArgs = [Any || _ <- Args],
GenSig = {AnyArgs, fun(_) -> t_any() end},
@@ -573,11 +575,16 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
NewAccRet = t_sup(AccRet, TotalRet),
?debug("NewAccRet: ~s\n", [t_to_string(NewAccRet)]),
handle_apply_or_call(Left, Args, ArgTypes, Map, Tree,
- State3, NewAccArgTypes, NewAccRet);
+ State3, NewAccArgTypes, NewAccRet, HadExternal);
handle_apply_or_call([], Args, _ArgTypes, Map, _Tree, State,
- AccArgTypes, AccRet) ->
- NewMap = enter_type_lists(Args, AccArgTypes, Map),
- {State, NewMap, AccRet}.
+ AccArgTypes, AccRet, HadExternal) ->
+ case HadExternal of
+ false ->
+ NewMap = enter_type_lists(Args, AccArgTypes, Map),
+ {State, NewMap, AccRet};
+ true ->
+ {had_external, State}
+ end.
apply_fail_reason(FailedSig, FailedBif, FailedContract) ->
if
diff --git a/lib/dialyzer/src/dialyzer_dep.erl b/lib/dialyzer/src/dialyzer_dep.erl
index f1ac41ff04..572e60278d 100644
--- a/lib/dialyzer/src/dialyzer_dep.erl
+++ b/lib/dialyzer/src/dialyzer_dep.erl
@@ -55,11 +55,11 @@
%%
%% Letrecs = a dict mapping var labels to their recursive definition.
%% top-level letrecs are not included as they are handled
-%% separatedly.
+%% separately.
%%
-spec analyze(cerl:c_module()) ->
- {dict:dict(), ordset('external' | label()), dict:dict(), dict:dict()}.
+ {dict:dict(), ordsets:ordset('external' | label()), dict:dict(), dict:dict()}.
analyze(Tree) ->
%% io:format("Handling ~w\n", [cerl:atom_val(cerl:module_name(Tree))]),
@@ -124,8 +124,10 @@ traverse(Tree, Out, State, CurrentFun) ->
TmpState = state__add_deps(Label, O1, State),
state__add_deps(CurrentFun, O2,TmpState)
end,
- {BodyFuns, State2} = traverse(Body, Out, State1,
- cerl_trees:get_label(Tree)),
+ Vars = cerl:fun_vars(Tree),
+ Out1 = bind_single(Vars, output(set__singleton(external)), Out),
+ {BodyFuns, State2} =
+ traverse(Body, Out1, State1, cerl_trees:get_label(Tree)),
{output(set__singleton(Label)), state__add_esc(BodyFuns, State2)};
'let' ->
Vars = cerl:let_vars(Tree),
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index 08f31c1e13..868857d675 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -61,7 +61,7 @@
init_plt :: dialyzer_plt:plt(),
dir_entry :: wx:wx_object(),
file_box :: wx:wx_object(),
- files_to_analyze :: ordset(string()),
+ files_to_analyze :: ordsets:ordset(string()),
gui :: wx:wx_object(),
log :: wx:wx_object(),
menu :: menu(),
@@ -699,8 +699,7 @@ handle_add_files(#gui_state{chosen_box = ChosenBox, file_box = FileBox,
end.
handle_add_dir(#gui_state{chosen_box = ChosenBox, dir_entry = DirBox,
- files_to_analyze = FileList,
- mode = Mode} = State) ->
+ files_to_analyze = FileList, mode = Mode} = State) ->
case wxDirPickerCtrl:getPath(DirBox) of
"" ->
State;
@@ -714,8 +713,8 @@ handle_add_dir(#gui_state{chosen_box = ChosenBox, dir_entry = DirBox,
State#gui_state{files_to_analyze = add_files(filter_mods(NewDir1,Ext), FileList, ChosenBox, Ext)}
end.
-handle_add_rec(#gui_state{chosen_box = ChosenBox, dir_entry = DirBox, files_to_analyze = FileList,
- mode = Mode} = State) ->
+handle_add_rec(#gui_state{chosen_box = ChosenBox, dir_entry = DirBox,
+ files_to_analyze = FileList, mode = Mode} = State) ->
case wxDirPickerCtrl:getPath(DirBox) of
"" ->
State;
@@ -723,11 +722,11 @@ handle_add_rec(#gui_state{chosen_box = ChosenBox, dir_entry = DirBox, files_to_a
NewDir = ordsets:new(),
NewDir1 = ordsets:add_element(Dir,NewDir),
TargetDirs = ordsets:union(NewDir1, all_subdirs(NewDir1)),
- case wxRadioBox:getSelection(Mode) of
- 0 -> Ext = ".beam";
- 1-> Ext = ".erl"
- end,
- State#gui_state{files_to_analyze = add_files(filter_mods(TargetDirs,Ext), FileList, ChosenBox, Ext)}
+ Ext = case wxRadioBox:getSelection(Mode) of
+ 0 -> ".beam";
+ 1 -> ".erl"
+ end,
+ State#gui_state{files_to_analyze = add_files(filter_mods(TargetDirs, Ext), FileList, ChosenBox, Ext)}
end.
handle_file_delete(#gui_state{chosen_box = ChosenBox,
@@ -886,13 +885,10 @@ config_gui_start(State) ->
wxRadioBox:disable(State#gui_state.mode).
save_file(#gui_state{frame = Frame, warnings_box = WBox, log = Log} = State, Type) ->
- case Type of
- warnings ->
- Message = "Save Warnings",
- Box = WBox;
- log -> Message = "Save Log",
- Box = Log
- end,
+ {Message, Box} = case Type of
+ warnings -> {"Save Warnings", WBox};
+ log -> {"Save Log", Log}
+ end,
case wxTextCtrl:getValue(Box) of
"" -> error_sms(State,"There is nothing to save...\n");
_ ->
@@ -936,8 +932,7 @@ include_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) ->
wxButton:connect(DeleteAllButton, command_button_clicked),
wxButton:connect(Ok, command_button_clicked),
wxButton:connect(Cancel, command_button_clicked),
- Dirs = [io_lib:format("~s", [X])
- || X <- Options#options.include_dirs],
+ Dirs = [io_lib:format("~s", [X]) || X <- Options#options.include_dirs],
wxListBox:set(Box, Dirs),
Layout = wxBoxSizer:new(?wxVERTICAL),
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl
index 06672e595f..a92b8b1958 100644
--- a/lib/dialyzer/src/dialyzer_options.erl
+++ b/lib/dialyzer/src/dialyzer_options.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -51,7 +51,8 @@ build(Opts) ->
?WARN_CONTRACT_TYPES,
?WARN_CONTRACT_SYNTAX,
?WARN_BEHAVIOUR,
- ?WARN_UNDEFINED_CALLBACK],
+ ?WARN_UNDEFINED_CALLBACK,
+ ?WARN_UNKNOWN],
DefaultWarns1 = ordsets:from_list(DefaultWarns),
InitPlt = dialyzer_plt:get_default_plt(),
DefaultOpts = #options{},
@@ -310,6 +311,8 @@ build_warnings([Opt|Opts], Warnings) ->
ordsets:add_element(?WARN_CONTRACT_SUBTYPE, Warnings);
underspecs ->
ordsets:add_element(?WARN_CONTRACT_SUPERTYPE, Warnings);
+ no_unknown ->
+ ordsets:del_element(?WARN_UNKNOWN, Warnings);
OtherAtom ->
bad_option("Unknown dialyzer warning option", OtherAtom)
end,
diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl
index 48fcde8014..28c2ad2c0b 100644
--- a/lib/dialyzer/src/dialyzer_races.erl
+++ b/lib/dialyzer/src/dialyzer_races.erl
@@ -85,6 +85,12 @@
-type race_tag() :: 'whereis_register' | 'whereis_unregister'
| 'ets_lookup_insert' | 'mnesia_dirty_read_write'.
+%% The following type is similar to the dial_warning() type but has a
+%% tag which is local to this module and is not propagated to outside
+-type dial_race_warning() :: {race_warn_tag(), file_line(), {atom(), [term()]}}.
+-type race_warn_tag() :: ?WARN_WHEREIS_REGISTER | ?WARN_WHEREIS_UNREGISTER
+ | ?WARN_ETS_LOOKUP_INSERT | ?WARN_MNESIA_DIRTY_READ_WRITE.
+
-record(beg_clause, {arg :: var_to_map1(),
pats :: var_to_map1(),
guard :: cerl:cerl()}).
@@ -103,7 +109,7 @@
args :: args(),
arg_types :: [erl_types:erl_type()],
vars :: [core_vars()],
- state :: _, %% XXX: recursive
+ state :: dialyzer_dataflow:state(),
file_line :: file_line(),
var_map :: dict:dict()}).
-record(fun_call, {caller :: dialyzer_callgraph:mfa_or_funlbl(),
@@ -141,7 +147,7 @@
race_tags = [] :: [#race_fun{}],
%% true for fun types and warning mode
race_analysis = false :: boolean(),
- race_warnings = [] :: [dial_warning()]}).
+ race_warnings = [] :: [dial_race_warning()]}).
%%% ===========================================================================
%%%
@@ -984,8 +990,7 @@ fixup_race_forward_helper(CurrFun, CurrFunLabel, Fun, FunLabel,
NewRaceVarMap, Args, NewFunArgs, NewFunTypes, NestingLevel};
{CurrFun, Fun} ->
NewCallsToAnalyze = lists:delete(Head, CallsToAnalyze),
- NewRaceVarMap =
- race_var_map(Args, NewFunArgs, RaceVarMap, bind),
+ NewRaceVarMap = race_var_map(Args, NewFunArgs, RaceVarMap, bind),
RetC =
case Fun of
InitFun ->
@@ -1012,8 +1017,7 @@ fixup_race_forward_helper(CurrFun, CurrFunLabel, Fun, FunLabel,
label = FunLabel, var_map = NewRaceVarMap,
def_vars = Args, call_vars = NewFunArgs,
arg_types = NewFunTypes}|
- lists:reverse(StateRaceList)] ++
- RetC;
+ lists:reverse(StateRaceList)] ++ RetC;
_ ->
[#curr_fun{status = in, mfa = Fun,
label = FunLabel, var_map = NewRaceVarMap,
@@ -1048,13 +1052,9 @@ fixup_race_backward(CurrFun, Calls, CallsToAnalyze, Parents, Height) ->
false -> [CurrFun|Parents]
end;
[Head|Tail] ->
- MorePaths =
- case Head of
- {Parent, CurrFun} -> true;
- {Parent, _TupleB} -> false
- end,
- case MorePaths of
- true ->
+ {Parent, TupleB} = Head,
+ case TupleB =:= CurrFun of
+ true -> % more paths are needed
NewCallsToAnalyze = lists:delete(Head, CallsToAnalyze),
NewParents =
fixup_race_backward(Parent, NewCallsToAnalyze,
@@ -1763,7 +1763,7 @@ ets_list_args(MaybeList) ->
catch _:_ -> [?no_label]
end;
false -> [ets_tuple_args(MaybeList)]
- end.
+ end.
ets_list_argtypes(ListStr) ->
ListStr1 = string:strip(ListStr, left, $[),
diff --git a/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
index 3ff26b87db..44a65f6e90 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
@@ -1 +1 @@
-{dialyzer_options, [{warnings, [no_unused, no_return]}]}.
+{dialyzer_options, [{warnings, [no_unused, no_return, no_unknown]}]}.
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/modules/opaque_erl_scan.erl b/lib/dialyzer/test/opaque_SUITE_data/src/modules/opaque_erl_scan.erl
index 9ecd4f92a1..24d0793a7c 100644
--- a/lib/dialyzer/test/opaque_SUITE_data/src/modules/opaque_erl_scan.erl
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/modules/opaque_erl_scan.erl
@@ -1,4 +1,4 @@
-%% -*- coding: utf-8 -*-
+%%
%%
%% %CopyrightBegin%
%%
diff --git a/lib/dialyzer/test/options1_SUITE_data/dialyzer_options b/lib/dialyzer/test/options1_SUITE_data/dialyzer_options
index c612e77d3e..65d233ac0d 100644
--- a/lib/dialyzer/test/options1_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/options1_SUITE_data/dialyzer_options
@@ -1,2 +1,2 @@
-{dialyzer_options, [{include_dirs, ["my_include"]}, {defines, [{'COMPILER_VSN', 42}]}, {warnings, [no_improper_lists]}]}.
+{dialyzer_options, [{include_dirs, ["my_include"]}, {defines, [{'COMPILER_VSN', 42}]}, {warnings, [no_improper_lists, no_unknown]}]}.
{time_limit, 30}.
diff --git a/lib/dialyzer/test/r9c_SUITE_data/dialyzer_options b/lib/dialyzer/test/r9c_SUITE_data/dialyzer_options
index e00e23bb66..ba0e6b1ad7 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/r9c_SUITE_data/dialyzer_options
@@ -1,2 +1,2 @@
-{dialyzer_options, [{defines, [{vsn, 42}]}]}.
+{dialyzer_options, [{defines, [{vsn, 42}]}, {warnings, [no_unknown]}]}.
{time_limit, 20}.
diff --git a/lib/dialyzer/test/race_SUITE_data/dialyzer_options b/lib/dialyzer/test/race_SUITE_data/dialyzer_options
index 44e1720715..6992fc6c40 100644
--- a/lib/dialyzer/test/race_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/race_SUITE_data/dialyzer_options
@@ -1 +1 @@
-{dialyzer_options, [{warnings, [race_conditions]}]}.
+{dialyzer_options, [{warnings, [race_conditions, no_unknown]}]}.
diff --git a/lib/dialyzer/test/small_SUITE_data/dialyzer_options b/lib/dialyzer/test/small_SUITE_data/dialyzer_options
index 50991c9bc5..0d91699e4d 100644
--- a/lib/dialyzer/test/small_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/small_SUITE_data/dialyzer_options
@@ -1 +1 @@
-{dialyzer_options, []}.
+{dialyzer_options, [{warnings, [no_unknown]}]}.
diff --git a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
index bfa33cd296..fbdd182358 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
+++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
@@ -1,27 +1,28 @@
contracts_with_subtypes.erl:106: The call contracts_with_subtypes:rec_arg({'a','b'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
contracts_with_subtypes.erl:107: The call contracts_with_subtypes:rec_arg({'b','a'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
-contracts_with_subtypes.erl:108: The call contracts_with_subtypes:rec_arg({'a',{'b','a'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
contracts_with_subtypes.erl:109: The call contracts_with_subtypes:rec_arg({'b',{'a','b'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
-contracts_with_subtypes.erl:110: The call contracts_with_subtypes:rec_arg({'a',{'b',{'a','b'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
-contracts_with_subtypes.erl:111: The call contracts_with_subtypes:rec_arg({'b',{'a',{'b','a'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A})
-contracts_with_subtypes.erl:142: The pattern 1 can never match the type string()
-contracts_with_subtypes.erl:145: The pattern 'alpha' can never match the type {'ok',_} | {'ok',_,string()}
-contracts_with_subtypes.erl:147: The pattern 42 can never match the type {'ok',_} | {'ok',_,string()}
-contracts_with_subtypes.erl:163: The pattern 'alpha' can never match the type {'ok',_}
-contracts_with_subtypes.erl:165: The pattern 42 can never match the type {'ok',_}
-contracts_with_subtypes.erl:183: The pattern 'alpha' can never match the type {'ok',_}
-contracts_with_subtypes.erl:185: The pattern 42 can never match the type {'ok',_}
-contracts_with_subtypes.erl:202: The pattern 1 can never match the type string()
-contracts_with_subtypes.erl:205: The pattern {'ok', _} can never match the type {'ok',_,string()}
-contracts_with_subtypes.erl:206: The pattern 'alpha' can never match the type {'ok',_,string()}
-contracts_with_subtypes.erl:207: The pattern {'ok', 42} can never match the type {'ok',_,string()}
-contracts_with_subtypes.erl:208: The pattern 42 can never match the type {'ok',_,string()}
-contracts_with_subtypes.erl:234: Function flat_ets_new_t/0 has no local return
-contracts_with_subtypes.erl:235: The call contracts_with_subtypes:flat_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,'set' | 'ordered_set' | 'bag' | 'duplicate_bag' | 'public' | 'protected' | 'private' | 'named_table' | {'keypos',integer()} | {'heir',pid(),term()} | {'heir','none'} | {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed')
+contracts_with_subtypes.erl:135: The call contracts_with_subtypes:rec2({'a','b'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,ab())
+contracts_with_subtypes.erl:136: The call contracts_with_subtypes:rec2({'b','a'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,ab())
+contracts_with_subtypes.erl:137: The call contracts_with_subtypes:rec2({'a',{'b','a'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,ab())
+contracts_with_subtypes.erl:138: The call contracts_with_subtypes:rec2({'b',{'a','b'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,ab())
+contracts_with_subtypes.erl:171: The pattern 1 can never match the type string()
+contracts_with_subtypes.erl:174: The pattern 'alpha' can never match the type {'ok',_} | {'ok',_,string()}
+contracts_with_subtypes.erl:176: The pattern 42 can never match the type {'ok',_} | {'ok',_,string()}
+contracts_with_subtypes.erl:192: The pattern 'alpha' can never match the type {'ok',_}
+contracts_with_subtypes.erl:194: The pattern 42 can never match the type {'ok',_}
+contracts_with_subtypes.erl:212: The pattern 'alpha' can never match the type {'ok',_}
+contracts_with_subtypes.erl:214: The pattern 42 can never match the type {'ok',_}
+contracts_with_subtypes.erl:231: The pattern 1 can never match the type string()
+contracts_with_subtypes.erl:234: The pattern {'ok', _} can never match the type {'ok',_,string()}
+contracts_with_subtypes.erl:235: The pattern 'alpha' can never match the type {'ok',_,string()}
+contracts_with_subtypes.erl:236: The pattern {'ok', 42} can never match the type {'ok',_,string()}
+contracts_with_subtypes.erl:237: The pattern 42 can never match the type {'ok',_,string()}
contracts_with_subtypes.erl:23: Invalid type specification for function contracts_with_subtypes:extract2/0. The success typing is () -> 'something'
-contracts_with_subtypes.erl:261: Function factored_ets_new_t/0 has no local return
-contracts_with_subtypes.erl:262: The call contracts_with_subtypes:factored_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,Type | Access | 'named_table' | {'keypos',Pos} | {'heir',Pid::pid(),HeirData} | {'heir','none'} | Tweaks), is_subtype(Type,type()), is_subtype(Access,access()), is_subtype(Tweaks,{'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed'), is_subtype(Pos,pos_integer()), is_subtype(HeirData,term())
+contracts_with_subtypes.erl:263: Function flat_ets_new_t/0 has no local return
+contracts_with_subtypes.erl:264: The call contracts_with_subtypes:flat_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,'set' | 'ordered_set' | 'bag' | 'duplicate_bag' | 'public' | 'protected' | 'private' | 'named_table' | {'keypos',integer()} | {'heir',pid(),term()} | {'heir','none'} | {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed')
+contracts_with_subtypes.erl:290: Function factored_ets_new_t/0 has no local return
+contracts_with_subtypes.erl:291: The call contracts_with_subtypes:factored_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,Type | Access | 'named_table' | {'keypos',Pos} | {'heir',Pid::pid(),HeirData} | {'heir','none'} | Tweaks), is_subtype(Type,type()), is_subtype(Access,access()), is_subtype(Tweaks,{'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed'), is_subtype(Pos,pos_integer()), is_subtype(HeirData,term())
contracts_with_subtypes.erl:77: The call contracts_with_subtypes:foo1(5) breaks the contract (Arg1) -> Res when is_subtype(Arg1,atom()), is_subtype(Res,atom())
contracts_with_subtypes.erl:78: The call contracts_with_subtypes:foo2(5) breaks the contract (Arg1) -> Res when is_subtype(Arg1,Arg2), is_subtype(Arg2,atom()), is_subtype(Res,atom())
contracts_with_subtypes.erl:79: The call contracts_with_subtypes:foo3(5) breaks the contract (Arg1) -> Res when is_subtype(Arg2,atom()), is_subtype(Arg1,Arg2), is_subtype(Res,atom())
diff --git a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes2 b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes2
new file mode 100644
index 0000000000..9f5433a13d
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes2
@@ -0,0 +1,3 @@
+
+contracts_with_subtypes2.erl:18: Function t/0 has no local return
+contracts_with_subtypes2.erl:19: The call contracts_with_subtypes2:t({'a',{'b',{'c',{'d',{'e',{'g',3}}}}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A}), is_subtype(A,{'b',B}), is_subtype(B,{'c',C}), is_subtype(C,{'d',D}), is_subtype(D,{'e',E}), is_subtype(E,{'f',_})
diff --git a/lib/dialyzer/test/small_SUITE_data/results/funs_from_outside b/lib/dialyzer/test/small_SUITE_data/results/funs_from_outside
new file mode 100644
index 0000000000..3e597ef1bc
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/funs_from_outside
@@ -0,0 +1,7 @@
+
+funs_from_outside.erl:18: The pattern 'error' can never match the type {'ok','nothing' | 'something'}
+funs_from_outside.erl:32: Function run2/2 has no local return
+funs_from_outside.erl:35: Function testb/3 has no local return
+funs_from_outside.erl:41: The pattern 'error' can never match the type {'ok','nothing' | 'something'}
+funs_from_outside.erl:78: Function test2/1 has no local return
+funs_from_outside.erl:83: The pattern 'error' can never match the type 'ok'
diff --git a/lib/dialyzer/test/small_SUITE_data/results/invalid_spec_2 b/lib/dialyzer/test/small_SUITE_data/results/invalid_spec_2
new file mode 100644
index 0000000000..4565112ea0
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/invalid_spec_2
@@ -0,0 +1,2 @@
+
+scala_user.erl:5: Invalid type specification for function scala_user:is_list/2. The success typing is (maybe_improper_list() | tuple(),_) -> boolean()
diff --git a/lib/dialyzer/test/small_SUITE_data/results/maps_difftype b/lib/dialyzer/test/small_SUITE_data/results/maps_difftype
new file mode 100644
index 0000000000..8980321135
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/maps_difftype
@@ -0,0 +1,3 @@
+
+maps_difftype.erl:10: Function empty_mismatch/1 has no local return
+maps_difftype.erl:11: The pattern ~{}~ can never match the type tuple()
diff --git a/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl
index d72138d509..d7dfd9752e 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl
@@ -103,15 +103,44 @@ c(babb) -> rec_arg({b, {a, {b, b}}});
c(ababb) -> rec_arg({a, {b, {a, {b, b}}}});
c(babaa) -> rec_arg({b, {a, {b, {a, a}}}}).
-w(ab) -> rec_arg({a, b});
-w(ba) -> rec_arg({b, a});
-w(aba) -> rec_arg({a, {b, a}});
-w(bab) -> rec_arg({b, {a, b}});
-w(abab) -> rec_arg({a, {b, {a, b}}});
-w(baba) -> rec_arg({b, {a, {b, a}}});
+w(ab) -> rec_arg({a, b}); % breaks the contract
+w(ba) -> rec_arg({b, a}); % breaks the contract
+w(aba) -> rec_arg({a, {b, a}}); % no longer breaks the contract
+w(bab) -> rec_arg({b, {a, b}}); % breaks the contract
+w(abab) -> rec_arg({a, {b, {a, b}}}); % no longer breaks the contract
+w(baba) -> rec_arg({b, {a, {b, a}}}); % no longer breaks the contract
w(ababa) -> rec_arg({a, {b, {a, {b, a}}}});
w(babab) -> rec_arg({b, {a, {b, {a, b}}}}).
+%% For comparison: the same thing with types
+
+-type ab() :: {a, a()} | {b, b()}.
+-type a() :: a | {b, b()}.
+-type b() :: b | {a, a()}.
+
+-spec rec2(Arg) -> ok when
+ Arg :: ab().
+
+rec2(X) -> get(X).
+
+d(aa) -> rec2({a, a});
+d(bb) -> rec2({b, b});
+d(abb) -> rec2({a, {b, b}});
+d(baa) -> rec2({b, {a, a}});
+d(abaa) -> rec2({a, {b, {a, a}}});
+d(babb) -> rec2({b, {a, {b, b}}});
+d(ababb) -> rec2({a, {b, {a, {b, b}}}});
+d(babaa) -> rec2({b, {a, {b, {a, a}}}}).
+
+q(ab) -> rec2({a, b}); % breaks the contract
+q(ba) -> rec2({b, a}); % breaks the contract
+q(aba) -> rec2({a, {b, a}}); % breaks the contract
+q(bab) -> rec2({b, {a, b}}); % breaks the contract
+q(abab) -> rec2({a, {b, {a, b}}});
+q(baba) -> rec2({b, {a, {b, a}}});
+q(ababa) -> rec2({a, {b, {a, {b, a}}}});
+q(babab) -> rec2({b, {a, {b, {a, b}}}}).
+
%===============================================================================
-type dublo(X) :: {X, X}.
@@ -143,7 +172,7 @@ st(X) when is_atom(X) ->
_Other -> ok
end;
alpha -> bad;
- {ok, 42} -> bad;
+ {ok, 42} -> ok;
42 -> bad
end.
@@ -161,7 +190,7 @@ dt(X) when is_atom(X) ->
err2 -> ok;
{ok, X} -> ok;
alpha -> bad;
- {ok, 42} -> bad;
+ {ok, 42} -> ok;
42 -> bad
end.
@@ -181,7 +210,7 @@ dt2(X) when is_atom(X) ->
err2 -> ok;
{ok, X} -> ok;
alpha -> bad;
- {ok, 42} -> bad;
+ {ok, 42} -> ok;
42 -> bad
end.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes2.erl b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes2.erl
new file mode 100644
index 0000000000..d2f945b284
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes2.erl
@@ -0,0 +1,40 @@
+-module(contracts_with_subtypes2).
+
+-compile(export_all).
+
+-behaviour(supervisor).
+
+-spec t(Arg) -> ok when
+ Arg :: {a, A},
+ A :: {b, B},
+ B :: {c, C},
+ C :: {d, D},
+ D :: {e, E},
+ E :: {f, _}.
+
+t(X) ->
+ get(X).
+
+t() ->
+ t({a, {b, {c, {d, {e, {g, 3}}}}}}). % breaks the contract
+
+%% This one should possibly result in warnings about unused variables.
+-spec l() -> ok when
+ X :: Y,
+ Y :: X.
+
+l() ->
+ ok.
+
+%% This is the example from seq12547 (ticket OTP-11798).
+%% There used to be a warning.
+
+-spec init(term()) -> Result when
+ Result :: {ok, {{supervisor:strategy(),
+ non_neg_integer(),
+ pos_integer()},
+ [supervisor:child_spec()]}}
+ | ignore.
+
+init(_) ->
+ foo:bar().
diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/common_types.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/common_types.hrl
new file mode 100644
index 0000000000..f362a06bca
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/common_types.hrl
@@ -0,0 +1,6 @@
+-type host() :: nonempty_string().
+-type path() :: nonempty_string().
+-type url() :: binary().
+
+% The host portion of a url, if available.
+-type url_host() :: host() | none.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/config.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/config.hrl
new file mode 100644
index 0000000000..8cab65fc9c
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/config.hrl
@@ -0,0 +1,148 @@
+
+-define(SECOND, 1000).
+-define(MINUTE, (60 * ?SECOND)).
+-define(HOUR, (60 * ?MINUTE)).
+-define(DAY, (24 * ?HOUR)).
+-define(MB, (1024 * 1024)).
+
+% Maximum length of tag/blob prefix
+-define(NAME_MAX, 511).
+
+% How long ddfs node startup can take. The most time-consuming part
+% is the scanning of the tag objects in the node's DDFS volumes.
+-define(NODE_STARTUP, (1 * ?MINUTE)).
+
+% How long to wait on the master for replies from nodes.
+-define(NODE_TIMEOUT, (10 * ?SECOND)).
+
+% How long to wait for a reply from an operation coordinated by the
+% master that accesses nodes. This value should be larger than
+% NODE_TIMEOUT.
+-define(NODEOP_TIMEOUT, (1 * ?MINUTE)).
+
+% The minimum amount of free space a node must have, to be considered
+% a primary candidate host for a new blob.
+-define(MIN_FREE_SPACE, (1024 * ?MB)).
+
+% The maximum number of active HTTP connections on a system (this
+% applies separately for GET and PUT operations).
+-define(HTTP_MAX_ACTIVE, 3).
+
+% The maximum number of waiting HTTP connections to queue up on a busy system.
+-define(HTTP_QUEUE_LENGTH, 100).
+
+% The maximum number of simultaneous HTTP connections. Note that
+% HTTP_MAX_CONNS * 2 * 2 + 32 < Maximum number of file descriptors, where
+% 2 = Get and put, 2 = two FDs required for each connection (connection
+% itself + a file it accesses), 32 = a guess how many extra fds is needed.
+-define(HTTP_MAX_CONNS, 128).
+
+% How long to keep a PUT request in queue if the system is busy.
+-define(PUT_WAIT_TIMEOUT, (1 * ?MINUTE)).
+
+% How long to keep a GET request in queue if the system is busy.
+-define(GET_WAIT_TIMEOUT, (1 * ?MINUTE)).
+
+% An unused loaded tag expires in TAG_EXPIRES milliseconds. Note that
+% if TAG_EXPIRES is not smaller than GC_INTERVAL, tags will never
+% expire from the memory cache and will always take up memory.
+-define(TAG_EXPIRES, (10 * ?HOUR)).
+
+% How often the master's cache of all known tag names is refreshed.
+% This refresh is only needed to purge deleted tags eventually from
+% the tag cache. It doesn't harm to have a long interval.
+-define(TAG_CACHE_INTERVAL, (10 * ?MINUTE)).
+
+% How soon a tag object initialized in memory expires if it's content
+% cannot be fetched from the cluster.
+-define(TAG_EXPIRES_ONERROR, (1 * ?SECOND)).
+
+% How often a DDFS node should refresh its tag cache from disk.
+-define(FIND_TAGS_INTERVAL, ?DAY).
+
+% How often buffered (delayed) updates to a tag need to be
+% flushed. Tradeoff: The longer the interval, the more updates are
+% bundled in a single commit. On the other hand, in the worst case
+% the requester has to wait for the full interval before getting a
+% reply. A long interval also increases the likelihood that the server
+% crashes before the commit has finished successfully, making requests
+% more unreliable.
+-define(DELAYED_FLUSH_INTERVAL, (1 * ?SECOND)).
+
+% How long to wait between garbage collection runs.
+-define(GC_INTERVAL, ?DAY).
+
+% Max duration for a GC run. This should be smaller than
+% min(ORPHANED_{BLOB,TAG}_EXPIRES).
+-define(GC_MAX_DURATION, (3 * ?DAY)).
+
+% How long to wait after startup for cluster to stabilize before
+% starting the first GC run.
+-define(GC_DEFAULT_INITIAL_WAIT, (5 * ?MINUTE)).
+
+% The longest potential interval between messages in the GC protocol;
+% used to ensure GC makes forward progress. This can be set to the
+% estimated time to traverse all the volumes on a DDFS node.
+-define(GC_PROGRESS_INTERVAL, (30 * ?MINUTE)).
+
+% Number of extra replicas (i.e. lost replicas recovered during GC) to
+% allow before deleting extra replicas.
+-define(NUM_EXTRA_REPLICAS, 1).
+
+% Permissions for files backing blobs and tags.
+-define(FILE_MODE, 8#00400).
+
+% How often to check available disk space in ddfs_node.
+-define(DISKSPACE_INTERVAL, (10 * ?SECOND)).
+
+% The maximum size of payloads of HTTP requests to the /ddfs/tag/
+% prefix.
+-define(MAX_TAG_BODY_SIZE, (512 * ?MB)).
+
+% Tag attribute names and values have a limited size, and there
+% can be only a limited number of them.
+-define(MAX_TAG_ATTRIB_NAME_SIZE, 1024).
+-define(MAX_TAG_ATTRIB_VALUE_SIZE, 1024).
+-define(MAX_NUM_TAG_ATTRIBS, 1000).
+
+% How long HTTP requests that perform tag updates should wait to
+% finish (a long time).
+-define(TAG_UPDATE_TIMEOUT, ?DAY).
+
+% Timeout for re-replicating a single blob over HTTP PUT. This
+% depends on the largest blobs hosted by DDFS, and the speed of the
+% cluster network.
+-define(GC_PUT_TIMEOUT, (180 * ?MINUTE)).
+
+% Delete !partial files after this many milliseconds.
+-define(PARTIAL_EXPIRES, ?DAY).
+
+% When orphaned blob can be deleted. This should be large enough that
+% you can upload all the new blobs of a tag and perform the tag update
+% within this time.
+-define(ORPHANED_BLOB_EXPIRES, (5 * ?DAY)).
+
+% When orphaned tag can be deleted.
+-define(ORPHANED_TAG_EXPIRES, (5 * ?DAY)).
+
+% How long a tag has to stay on the deleted list before
+% we can permanently forget it, after all known instances
+% of the tag object have been removed. This quarantine period
+% ensures that a node that was temporarily unavailable
+% and reactivates can't resurrect deleted tags. You
+% must ensure that all temporarily inactive nodes
+% are reactivated (or cleaned) within the ?DELETED_TAG_EXPIRES
+% time frame.
+%
+% This value _must_ be larger than the other time-related DDFS
+% parameters listed in this file. In particular, it must be larger
+% than ORPHANED_TAG_EXPIRES.
+-define(DELETED_TAG_EXPIRES, (30 * ?DAY)).
+
+% How many times a tag operation should be retried before aborting.
+-define(MAX_TAG_OP_RETRIES, 3).
+
+% How long to wait before timing out a tag retrieval. This should be
+% large enough to read a large tag object off the disk and send it
+% over the network.
+-define(GET_TAG_TIMEOUT, (5 * ?MINUTE)).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs.hrl
new file mode 100644
index 0000000000..e43ec23fe1
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs.hrl
@@ -0,0 +1,9 @@
+-type volume_name() :: nonempty_string().
+
+% Diskinfo is {FreeSpace, UsedSpace}.
+-type diskinfo() :: {non_neg_integer(), non_neg_integer()}.
+-type volume() :: {diskinfo(), volume_name()}.
+
+-type object_type() :: 'blob' | 'tag'.
+-type object_name() :: binary().
+-type taginfo() :: {erlang:timestamp(), volume_name()}.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_gc.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_gc.hrl
new file mode 100644
index 0000000000..dc43f7586b
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_gc.hrl
@@ -0,0 +1,17 @@
+-type local_object() :: {object_name(), node()}.
+-type phase() :: 'start' | 'build_map' | 'map_wait' | 'gc'
+ | 'rr_blobs' | 'rr_blobs_wait' | 'rr_tags'.
+-type protocol_msg() :: {'check_blob', object_name()} | 'start_gc' | 'end_rr'.
+
+-type blob_update() :: {object_name(), 'filter' | [url()]}.
+
+-type check_blob_result() :: 'false' | {'true', volume_name()}.
+
+% GC statistics
+
+% {Files, Bytes}
+-type gc_stat() :: {non_neg_integer(), non_neg_integer()}.
+% {Kept, Deleted}
+-type obj_stats() :: {gc_stat(), gc_stat()}.
+% {Tags, Blobs}.
+-type gc_run_stats() :: {obj_stats(), obj_stats()}.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_master.erl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_master.erl
new file mode 100644
index 0000000000..2be2773dc5
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_master.erl
@@ -0,0 +1,531 @@
+-module(ddfs_master).
+-behaviour(gen_server).
+
+-export([start_link/0]).
+-export([get_tags/1, get_tags/3,
+ get_nodeinfo/1,
+ get_read_nodes/0,
+ get_hosted_tags/1,
+ gc_blacklist/0, gc_blacklist/1,
+ gc_stats/0,
+ choose_write_nodes/3,
+ new_blob/4, new_blob/5,
+ safe_gc_blacklist/0, safe_gc_blacklist/1,
+ refresh_tag_cache/0,
+ tag_notify/2,
+ tag_operation/2, tag_operation/3,
+ update_gc_stats/1,
+ update_nodes/1
+ ]).
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+-define(WEB_PORT, 8011).
+
+-compile(nowarn_deprecated_type).
+
+-include("common_types.hrl").
+-include("gs_util.hrl").
+-include("config.hrl").
+-include("ddfs.hrl").
+-include("ddfs_tag.hrl").
+-include("ddfs_gc.hrl").
+
+-type node_info() :: {node(), {non_neg_integer(), non_neg_integer()}}.
+-type gc_stats() :: none | gc_run_stats().
+
+-record(state, {tags = gb_trees:empty() :: gb_trees:tree(),
+ tag_cache = false :: false | gb_sets:set(),
+ cache_refresher :: pid(),
+
+ nodes = [] :: [node_info()],
+ write_blacklist = [] :: [node()],
+ read_blacklist = [] :: [node()],
+ gc_blacklist = [] :: [node()],
+ safe_gc_blacklist = gb_sets:empty() :: gb_sets:set(),
+ gc_stats = none :: none | {gc_stats(), erlang:timestamp()}}).
+-type state() :: #state{}.
+-type replyto() :: {pid(), reference()}.
+
+-export_type([gc_stats/0, node_info/0]).
+
+%% ===================================================================
+%% API functions
+
+-spec start_link() -> {ok, pid()}.
+start_link() ->
+ lager:info("DDFS master starts"),
+ case gen_server:start_link({local, ?MODULE}, ?MODULE, [], []) of
+ {ok, Server} -> {ok, Server};
+ {error, {already_started, Server}} -> {ok, Server}
+ end.
+
+-spec tag_operation(term(), tagname()) -> term().
+tag_operation(Op, Tag) ->
+ gen_server:call(?MODULE, {tag, Op, Tag}).
+-spec tag_operation(term(), tagname(), non_neg_integer() | infinity) ->
+ term().
+tag_operation(Op, Tag, Timeout) ->
+ gen_server:call(?MODULE, {tag, Op, Tag}, Timeout).
+
+-spec tag_notify(term(), tagname()) -> ok.
+tag_notify(Op, Tag) ->
+ gen_server:cast(?MODULE, {tag_notify, Op, Tag}).
+
+-spec get_nodeinfo(all) -> {ok, [node_info()]}.
+get_nodeinfo(all) ->
+ gen_server:call(?MODULE, {get_nodeinfo, all}).
+
+-spec get_read_nodes() -> {ok, [node()], non_neg_integer()} | {error, term()}.
+get_read_nodes() ->
+ gen_server:call(?MODULE, get_read_nodes, infinity).
+
+-spec gc_blacklist() -> {ok, [node()]}.
+gc_blacklist() ->
+ gen_server:call(?MODULE, gc_blacklist).
+
+-spec gc_blacklist([node()]) -> ok.
+gc_blacklist(Nodes) ->
+ gen_server:cast(?MODULE, {gc_blacklist, Nodes}).
+
+-spec gc_stats() -> {ok, none | {gc_stats(), erlang:timestamp()}} | {error, term()}.
+gc_stats() ->
+ gen_server:call(?MODULE, gc_stats).
+
+-spec get_hosted_tags(host()) -> {ok, [tagname()]} | {error, term()}.
+get_hosted_tags(Host) ->
+ gen_server:call(?MODULE, {get_hosted_tags, Host}).
+
+-spec choose_write_nodes(non_neg_integer(), [node()], [node()]) -> {ok, [node()]}.
+choose_write_nodes(K, Include, Exclude) ->
+ gen_server:call(?MODULE, {choose_write_nodes, K, Include, Exclude}).
+
+-spec get_tags(gc) -> {ok, [tagname()], [node()]} | too_many_failed_nodes;
+ (safe) -> {ok, [binary()]} | too_many_failed_nodes.
+get_tags(Mode) ->
+ get_tags(?MODULE, Mode, ?GET_TAG_TIMEOUT).
+
+-spec get_tags(server(), gc, non_neg_integer()) ->
+ {ok, [tagname()], [node()]} | too_many_failed_nodes;
+ (server(), safe, non_neg_integer()) ->
+ {ok, [binary()]} | too_many_failed_nodes.
+get_tags(Server, Mode, Timeout) ->
+ disco_profile:timed_run(
+ fun() -> gen_server:call(Server, {get_tags, Mode}, Timeout) end,
+ get_tags).
+
+-spec new_blob(string()|object_name(), non_neg_integer(), [node()], [node()]) ->
+ too_many_replicas | {ok, [nonempty_string()]}.
+new_blob(Obj, K, Include, Exclude) ->
+ gen_server:call(?MODULE, {new_blob, Obj, K, Include, Exclude}, infinity).
+
+-spec new_blob(server(), string()|object_name(), non_neg_integer(), [node()], [node()]) ->
+ too_many_replicas | {ok, [nonempty_string()]}.
+new_blob(Master, Obj, K, Include, Exclude) ->
+ gen_server:call(Master, {new_blob, Obj, K, Include, Exclude}, infinity).
+
+-spec safe_gc_blacklist() -> {ok, [node()]} | {error, term()}.
+safe_gc_blacklist() ->
+ gen_server:call(?MODULE, safe_gc_blacklist).
+
+-spec safe_gc_blacklist(gb_sets:set()) -> ok.
+safe_gc_blacklist(SafeGCBlacklist) ->
+ gen_server:cast(?MODULE, {safe_gc_blacklist, SafeGCBlacklist}).
+
+-spec update_gc_stats(gc_run_stats()) -> ok.
+update_gc_stats(Stats) ->
+ gen_server:cast(?MODULE, {update_gc_stats, Stats}).
+
+-type nodes_update() :: [{node(), boolean(), boolean()}].
+-spec update_nodes(nodes_update()) -> ok.
+update_nodes(DDFSNodes) ->
+ gen_server:cast(?MODULE, {update_nodes, DDFSNodes}).
+
+-spec update_nodestats(gb_trees:tree()) -> ok.
+update_nodestats(NewNodes) ->
+ gen_server:cast(?MODULE, {update_nodestats, NewNodes}).
+
+-spec update_tag_cache(gb_sets:set()) -> ok.
+update_tag_cache(TagCache) ->
+ gen_server:cast(?MODULE, {update_tag_cache, TagCache}).
+
+-spec refresh_tag_cache() -> ok.
+refresh_tag_cache() ->
+ gen_server:cast(?MODULE, refresh_tag_cache).
+
+%% ===================================================================
+%% gen_server callbacks
+
+-spec init(_) -> gs_init().
+init(_Args) ->
+ _ = [disco_profile:new_histogram(Name)
+ || Name <- [get_tags, do_get_tags_all, do_get_tags_filter,
+ do_get_tags_safe, do_get_tags_gc]],
+ spawn_link(fun() -> monitor_diskspace() end),
+ spawn_link(fun() -> ddfs_gc:start_gc(disco:get_setting("DDFS_DATA")) end),
+ Refresher = spawn_link(fun() -> refresh_tag_cache_proc() end),
+ put(put_port, disco:get_setting("DDFS_PUT_PORT")),
+ {ok, #state{cache_refresher = Refresher}}.
+
+-type choose_write_nodes_msg() :: {choose_write_nodes, non_neg_integer(), [node()], [node()]}.
+-type new_blob_msg() :: {new_blob, string() | object_name(), non_neg_integer(), [node()]}.
+-type tag_msg() :: {tag, ddfs_tag:call_msg(), tagname()}.
+-spec handle_call(dbg_state_msg(), from(), state()) ->
+ gs_reply(state());
+ ({get_nodeinfo, all}, from(), state()) ->
+ gs_reply({ok, [node_info()]});
+ (get_read_nodes, from(), state()) ->
+ gs_reply({ok, [node()], non_neg_integer});
+ (gc_blacklist, from(), state()) ->
+ gs_reply({ok, [node()]});
+ (gc_stats, from(), state()) ->
+ gs_reply({ok, gc_stats(), erlang:timestamp()});
+ (choose_write_nodes_msg(), from(), state()) ->
+ gs_reply({ok, [node()]});
+ (new_blob_msg(), from(), state()) ->
+ gs_reply(new_blob_result());
+ (tag_msg(), from(), state()) ->
+ gs_reply({error, nonodes}) | gs_noreply();
+ ({get_tags, gc | safe}, from(), state()) ->
+ gs_noreply();
+ ({get_hosted_tags, host()}, from(), state()) ->
+ gs_noreply();
+ (safe_gc_blacklist, from(), state()) ->
+ gs_reply({ok, [node()]}).
+handle_call(dbg_get_state, _, S) ->
+ {reply, S, S};
+
+handle_call({get_nodeinfo, all}, _From, #state{nodes = Nodes} = S) ->
+ {reply, {ok, Nodes}, S};
+
+handle_call(get_read_nodes, _F, #state{nodes = Nodes, read_blacklist = RB} = S) ->
+ {reply, do_get_readable_nodes(Nodes, RB), S};
+
+handle_call(gc_blacklist, _F, #state{gc_blacklist = Nodes} = S) ->
+ {reply, {ok, Nodes}, S};
+
+handle_call(gc_stats, _F, #state{gc_stats = Stats} = S) ->
+ {reply, {ok, Stats}, S};
+
+handle_call({choose_write_nodes, K, Include, Exclude}, _,
+ #state{nodes = N, write_blacklist = WBL, gc_blacklist = GBL} = S) ->
+ BL = lists:umerge(WBL, GBL),
+ {reply, do_choose_write_nodes(N, K, Include, Exclude, BL), S};
+
+handle_call({new_blob, Obj, K, Include, Exclude}, _,
+ #state{nodes = N, gc_blacklist = GBL, write_blacklist = WBL} = S) ->
+ BL = lists:umerge(WBL, GBL),
+ {reply, do_new_blob(Obj, K, Include, Exclude, BL, N), S};
+
+handle_call({tag, _M, _Tag}, _From, #state{nodes = []} = S) ->
+ {reply, {error, no_nodes}, S};
+
+handle_call({tag, M, Tag}, From, S) ->
+ {noreply, do_tag_request(M, Tag, From, S)};
+
+handle_call({get_tags, Mode}, From, #state{nodes = Nodes} = S) ->
+ spawn(fun() ->
+ gen_server:reply(From, do_get_tags(Mode, [N || {N, _} <- Nodes]))
+ end),
+ {noreply, S};
+
+handle_call({get_hosted_tags, Host}, From, S) ->
+ spawn(fun() -> gen_server:reply(From, ddfs_gc:hosted_tags(Host)) end),
+ {noreply, S};
+
+handle_call(safe_gc_blacklist, _From, #state{safe_gc_blacklist = SBL} = S) ->
+ {reply, {ok, gb_sets:to_list(SBL)}, S}.
+
+-spec handle_cast({tag_notify, ddfs_tag:cast_msg(), tagname()}
+ | {gc_blacklist, [node()]}
+ | {safe_gc_blacklist, gb_sets:set()}
+ | {update_gc_stats, gc_stats()}
+ | {update_tag_cache, gb_sets:set()}
+ | refresh_tag_cache
+ | {update_nodes, nodes_update()}
+ | {update_nodestats, gb_trees:tree()},
+ state()) -> gs_noreply().
+handle_cast({tag_notify, M, Tag}, S) ->
+ {noreply, do_tag_notify(M, Tag, S)};
+
+handle_cast({gc_blacklist, Nodes}, #state{safe_gc_blacklist = SBL} = S) ->
+ BLSet = gb_sets:from_list(Nodes),
+ NewSBL = gb_sets:intersection(BLSet, SBL),
+ {noreply, S#state{gc_blacklist = gb_sets:to_list(BLSet),
+ safe_gc_blacklist = NewSBL}};
+
+handle_cast({safe_gc_blacklist, SafeBlacklist}, #state{gc_blacklist = BL} = S) ->
+ SBL = gb_sets:intersection(SafeBlacklist, gb_sets:from_list(BL)),
+ {noreply, S#state{safe_gc_blacklist = SBL}};
+
+handle_cast({update_gc_stats, Stats}, S) ->
+ {noreply, S#state{gc_stats = {Stats, now()}}};
+
+handle_cast({update_tag_cache, TagCache}, S) ->
+ {noreply, S#state{tag_cache = TagCache}};
+
+handle_cast(refresh_tag_cache, #state{cache_refresher = Refresher} = S) ->
+ Refresher ! refresh,
+ {noreply, S};
+
+handle_cast({update_nodes, NewNodes}, S) ->
+ {noreply, do_update_nodes(NewNodes, S)};
+
+handle_cast({update_nodestats, NewNodes}, S) ->
+ {noreply, do_update_nodestats(NewNodes, S)}.
+
+-spec handle_info({'DOWN', _, _, pid(), _}, state()) -> gs_noreply().
+handle_info({'DOWN', _, _, Pid, _}, S) ->
+ {noreply, do_tag_exit(Pid, S)}.
+
+%% ===================================================================
+%% gen_server callback stubs
+
+-spec terminate(term(), state()) -> ok.
+terminate(Reason, _State) ->
+ lager:warning("DDFS master died: ~p", [Reason]).
+
+-spec code_change(term(), state(), term()) -> {ok, state()}.
+code_change(_OldVsn, State, _Extra) -> {ok, State}.
+
+%% ===================================================================
+%% internal functions
+
+-spec do_get_readable_nodes([node_info()], [node()]) ->
+ {ok, [node()], non_neg_integer()}.
+do_get_readable_nodes(Nodes, ReadBlacklist) ->
+ NodeSet = gb_sets:from_ordset(lists:sort([Node || {Node, _} <- Nodes])),
+ BlackSet = gb_sets:from_ordset(ReadBlacklist),
+ ReadableNodeSet = gb_sets:subtract(NodeSet, BlackSet),
+ {ok, gb_sets:to_list(ReadableNodeSet), gb_sets:size(BlackSet)}.
+
+-spec do_choose_write_nodes([node_info()], non_neg_integer(), [node()], [node()], [node()]) ->
+ {ok, [node()]}.
+do_choose_write_nodes(Nodes, K, Include, Exclude, BlackList) ->
+ % Include is the list of nodes that must be included
+ %
+ % Node selection algorithm:
+ % 1. try to choose K nodes randomly from all the nodes which have
+ % more than ?MIN_FREE_SPACE bytes free space available and which
+ % are not excluded or blacklisted.
+ % 2. if K nodes cannot be found this way, choose the K emptiest
+ % nodes which are not excluded or blacklisted.
+ Primary = ([N || {N, {Free, _Total}} <- Nodes, Free > ?MIN_FREE_SPACE / 1024]
+ -- (Exclude ++ BlackList)),
+ if length(Primary) >= K ->
+ {ok, Include ++ disco_util:choose_random(Primary -- Include , K - length(Include))};
+ true ->
+ Preferred = [N || {N, _} <- lists:reverse(lists:keysort(2, Nodes))],
+ Secondary = Include ++ lists:sublist(Preferred -- (Include ++ Exclude ++ BlackList),
+ K - length(Include)),
+ {ok, Secondary}
+ end.
+
+-type new_blob_result() :: too_many_replicas | {ok, [nonempty_string()]}.
+-spec do_new_blob(string()|object_name(), non_neg_integer(), [node()], [node()], [node()], [node_info()]) ->
+ new_blob_result().
+do_new_blob(_Obj, K, _Include, _Exclude, _BlackList, Nodes) when K > length(Nodes) ->
+ too_many_replicas;
+do_new_blob(Obj, K, Include, Exclude, BlackList, Nodes) ->
+ {ok, WriteNodes} = do_choose_write_nodes(Nodes, K, Include, Exclude, BlackList),
+ Urls = [["http://", disco:host(N), ":", get(put_port), "/ddfs/", Obj]
+ || N <- WriteNodes],
+ {ok, Urls}.
+
+% Tag request: Start a new tag server if one doesn't exist already. Forward
+% the request to the tag server.
+
+-spec get_tag_pid(tagname(), gb_trees:tree(), false | gb_sets:set()) ->
+ {pid(), gb_trees:tree()}.
+get_tag_pid(Tag, Tags, Cache) ->
+ case gb_trees:lookup(Tag, Tags) of
+ none ->
+ NotFound = (Cache =/= false
+ andalso not gb_sets:is_element(Tag, Cache)),
+ {ok, Server} = ddfs_tag:start(Tag, NotFound),
+ erlang:monitor(process, Server),
+ {Server, gb_trees:insert(Tag, Server, Tags)};
+ {value, P} ->
+ {P, Tags}
+ end.
+
+-spec do_tag_request(term(), tagname(), replyto(), state()) ->
+ state().
+do_tag_request(M, Tag, From, #state{tags = Tags, tag_cache = Cache} = S) ->
+ {Pid, TagsN} = get_tag_pid(Tag, Tags, Cache),
+ gen_server:cast(Pid, {M, From}),
+ S#state{tags = TagsN,
+ tag_cache = Cache =/= false andalso gb_sets:add(Tag, Cache)}.
+
+-spec do_tag_notify(term(), tagname(), state()) -> state().
+do_tag_notify(M, Tag, #state{tags = Tags, tag_cache = Cache} = S) ->
+ {Pid, TagsN} = get_tag_pid(Tag, Tags, Cache),
+ gen_server:cast(Pid, {notify, M}),
+ S#state{tags = TagsN,
+ tag_cache = Cache =/= false andalso gb_sets:add(Tag, Cache)}.
+
+-spec do_update_nodes(nodes_update(), state()) -> state().
+do_update_nodes(NewNodes, #state{nodes = Nodes, tags = Tags} = S) ->
+ WriteBlacklist = lists:sort([Node || {Node, false, _} <- NewNodes]),
+ ReadBlacklist = lists:sort([Node || {Node, _, false} <- NewNodes]),
+ OldNodes = gb_trees:from_orddict(Nodes),
+ UpdatedNodes = lists:keysort(1, [case gb_trees:lookup(Node, OldNodes) of
+ none ->
+ {Node, {0, 0}};
+ {value, OldStats} ->
+ {Node, OldStats}
+ end || {Node, _WB, _RB} <- NewNodes]),
+ if
+ UpdatedNodes =/= Nodes ->
+ _ = [gen_server:cast(Pid, {die, none}) || Pid <- gb_trees:values(Tags)],
+ spawn(fun() ->
+ {ok, ReadableNodes, RBSize} =
+ do_get_readable_nodes(UpdatedNodes, ReadBlacklist),
+ refresh_tag_cache(ReadableNodes, RBSize)
+ end),
+ S#state{nodes = UpdatedNodes,
+ write_blacklist = WriteBlacklist,
+ read_blacklist = ReadBlacklist,
+ tag_cache = false,
+ tags = gb_trees:empty()};
+ true ->
+ S#state{write_blacklist = WriteBlacklist,
+ read_blacklist = ReadBlacklist}
+ end.
+
+-spec do_update_nodestats(gb_trees:tree(), state()) -> state().
+do_update_nodestats(NewNodes, #state{nodes = Nodes} = S) ->
+ UpdatedNodes = [case gb_trees:lookup(Node, NewNodes) of
+ none ->
+ {Node, Stats};
+ {value, NewStats} ->
+ {Node, NewStats}
+ end || {Node, Stats} <- Nodes],
+ S#state{nodes = UpdatedNodes}.
+
+-spec do_tag_exit(pid(), state()) -> state().
+do_tag_exit(Pid, S) ->
+ NewTags = [X || {_, V} = X <- gb_trees:to_list(S#state.tags), V =/= Pid],
+ S#state{tags = gb_trees:from_orddict(NewTags)}.
+
+-spec do_get_tags(all | filter, [node()]) -> {[node()], [node()], [binary()]};
+ (safe, [node()]) -> {ok, [binary()]} | too_many_failed_nodes;
+ (gc, [node()]) -> {ok, [binary()], [node()]} | too_many_failed_nodes.
+do_get_tags(all, Nodes) ->
+ disco_profile:timed_run(
+ fun() ->
+ {Replies, Failed} =
+ gen_server:multi_call(Nodes, ddfs_node, get_tags, ?NODE_TIMEOUT),
+ {OkNodes, Tags} = lists:unzip(Replies),
+ {OkNodes, Failed, lists:usort(lists:flatten(Tags))}
+ end, do_get_tags_all);
+
+do_get_tags(filter, Nodes) ->
+ disco_profile:timed_run(
+ fun() ->
+ {OkNodes, Failed, Tags} = do_get_tags(all, Nodes),
+ case tag_operation(get_tagnames, <<"+deleted">>, ?NODEOP_TIMEOUT) of
+ {ok, Deleted} ->
+ TagSet = gb_sets:from_ordset(Tags),
+ DelSet = gb_sets:insert(<<"+deleted">>, Deleted),
+ NotDeleted = gb_sets:to_list(gb_sets:subtract(TagSet, DelSet)),
+ {OkNodes, Failed, NotDeleted};
+ E ->
+ E
+ end
+ end, do_get_tags_filter);
+
+do_get_tags(safe, Nodes) ->
+ disco_profile:timed_run(
+ fun() ->
+ TagMinK = list_to_integer(disco:get_setting("DDFS_TAG_MIN_REPLICAS")),
+ case do_get_tags(filter, Nodes) of
+ {_OkNodes, Failed, Tags} when length(Failed) < TagMinK ->
+ {ok, Tags};
+ _ ->
+ too_many_failed_nodes
+ end
+ end, do_get_tags_safe);
+
+% The returned tag list may include +deleted.
+do_get_tags(gc, Nodes) ->
+ disco_profile:timed_run(
+ fun() ->
+ {OkNodes, Failed, Tags} = do_get_tags(all, Nodes),
+ TagMinK = list_to_integer(disco:get_setting("DDFS_TAG_MIN_REPLICAS")),
+ case length(Failed) < TagMinK of
+ false ->
+ too_many_failed_nodes;
+ true ->
+ case tag_operation(get_tagnames, <<"+deleted">>, ?NODEOP_TIMEOUT) of
+ {ok, Deleted} ->
+ TagSet = gb_sets:from_ordset(Tags),
+ NotDeleted = gb_sets:subtract(TagSet, Deleted),
+ {ok, gb_sets:to_list(NotDeleted), OkNodes};
+ E ->
+ E
+ end
+ end
+ end, do_get_tags_gc).
+
+% Timeouts in this call by the below processes can cause ddfs_master
+% itself to crash, since the processes are linked to it.
+-spec safe_get_read_nodes() -> {ok, [node()], non_neg_integer()} | error.
+safe_get_read_nodes() ->
+ try get_read_nodes() of
+ {ok, _ReadableNodes, _RBSize} = RN ->
+ RN;
+ E ->
+ lager:error("unexpected response retrieving readable nodes: ~p", [E]),
+ error
+ catch
+ K:E ->
+ lager:error("error retrieving readable nodes: ~p:~p", [K, E]),
+ error
+ end.
+
+-spec monitor_diskspace() -> no_return().
+monitor_diskspace() ->
+ case safe_get_read_nodes() of
+ {ok, ReadableNodes, _RBSize} ->
+ {Space, _F} = gen_server:multi_call(ReadableNodes,
+ ddfs_node,
+ get_diskspace,
+ ?NODE_TIMEOUT),
+ update_nodestats(gb_trees:from_orddict(lists:keysort(1, Space)));
+ error ->
+ ok
+ end,
+ timer:sleep(?DISKSPACE_INTERVAL),
+ monitor_diskspace().
+
+-spec refresh_tag_cache_proc() -> no_return().
+refresh_tag_cache_proc() ->
+ case safe_get_read_nodes() of
+ {ok, ReadableNodes, RBSize} ->
+ refresh_tag_cache(ReadableNodes, RBSize);
+ error ->
+ ok
+ end,
+ receive
+ refresh ->
+ ok
+ after ?TAG_CACHE_INTERVAL ->
+ ok
+ end,
+ refresh_tag_cache_proc().
+
+-spec refresh_tag_cache([node()], non_neg_integer()) -> ok.
+refresh_tag_cache(Nodes, BLSize) ->
+ TagMinK = list_to_integer(disco:get_setting("DDFS_TAG_MIN_REPLICAS")),
+ {Replies, Failed} =
+ gen_server:multi_call(Nodes, ddfs_node, get_tags, ?NODE_TIMEOUT),
+ if Nodes =/= [], length(Failed) + BLSize < TagMinK ->
+ {_OkNodes, Tags} = lists:unzip(Replies),
+ update_tag_cache(gb_sets:from_list(lists:flatten(Tags)));
+ true -> ok
+ end.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_tag.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_tag.hrl
new file mode 100644
index 0000000000..2920b67fc5
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/ddfs_tag.hrl
@@ -0,0 +1,19 @@
+
+-type tokentype() :: 'read' | 'write'.
+-type user_attr() :: [{binary(), binary()}].
+% An 'internal' token is also used by internal consumers, but never stored.
+-type token() :: 'null' | binary().
+
+-type tagname() :: binary().
+-type tagid() :: binary().
+
+-type attrib() :: 'urls' | 'read_token' | 'write_token' | {'user', binary()}.
+
+-record(tagcontent, {id :: tagid(),
+ last_modified :: binary(),
+ read_token = null :: token(),
+ write_token = null :: token(),
+ urls = [] :: [[binary()]],
+ user = [] :: user_attr()}).
+
+-type tagcontent() :: #tagcontent{}.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/gs_util.hrl b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/gs_util.hrl
new file mode 100644
index 0000000000..d579e9a7d7
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/ddfs_master/gs_util.hrl
@@ -0,0 +1,16 @@
+% This is a set of type utilities to be used when spec-cing the
+% callbacks of a gen_server implementation. It should be included in
+% the impl module, which needs to define the state() type.
+
+-type gs_init() :: {ok, state()}.
+-type gs_reply(T) :: {reply, (T), state()}.
+-type gs_noreply() :: {noreply, state()}.
+-type gs_noreply_t() :: {noreply, state(), non_neg_integer()}.
+-type gs_stop(T) :: {stop, (T), state()}.
+
+% Generic utilities.
+
+-type server() :: pid() | atom() | {atom(), node()}.
+-type from() :: {pid(), term()}.
+
+-type dbg_state_msg() :: dbg_get_state.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/funs_from_outside.erl b/lib/dialyzer/test/small_SUITE_data/src/funs_from_outside.erl
new file mode 100644
index 0000000000..f4cbf31160
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/funs_from_outside.erl
@@ -0,0 +1,83 @@
+-module(funs_from_outside).
+
+-export([run1/2, run2/2, run3/2]).
+-export([test1/1, test2/1]).
+
+%%------------------------------------------------------------------------------
+
+run1(X, Y) ->
+ testa(fun do_something/1, X, Y).
+
+testa(Fun, X, Y) ->
+ F = case even(X) of
+ true -> Fun;
+ false -> fun do_nothing/1
+ end,
+ case F(Y) of
+ {ok, _} -> ok;
+ error -> error
+ end.
+
+do_nothing(_) -> {ok, nothing}.
+
+do_something(_) -> {ok, something}.
+
+even(X) ->
+ X rem 2 =:= 0.
+
+%%------------------------------------------------------------------------------
+
+%% Duplicating code since we are monovariant...
+
+run2(X, Y) ->
+ testb(fun do_something/1, X, Y).
+
+testb(Fun, X, Y) ->
+ F = case even(X) of
+ true -> Fun;
+ false -> fun do_nothing/1
+ end,
+ case F(Y) of
+ error -> error
+ end.
+
+%%------------------------------------------------------------------------------
+
+%% Duplicating code since we are monovariant...
+
+run3(X, Y) ->
+ testc(fun do_something_2/1, X, Y).
+
+testc(Fun, X, Y) ->
+ F = case even(X) of
+ true -> Fun;
+ false -> fun do_nothing/1
+ end,
+ case F(Y) of
+ {ok, _} -> ok;
+ %% This pattern can match.
+ error -> error
+ end.
+
+do_something_2(foo) -> {ok, something};
+do_something_2(_) -> error.
+
+%%------------------------------------------------------------------------------
+
+test1(Fun) ->
+ F = case get(test1) of
+ test1_t -> Fun;
+ test1_f -> fun fok/0
+ end,
+ error = F().
+
+fok() -> ok.
+
+%%------------------------------------------------------------------------------
+
+test2(Fun) ->
+ F = case get(test1) of
+ test1_t -> fun fok/0;
+ test1_f -> fun fok/0
+ end,
+ error = F().
diff --git a/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_data.erl b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_data.erl
new file mode 100644
index 0000000000..c26787fe24
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_data.erl
@@ -0,0 +1,5 @@
+-module(scala_data).
+
+-export_type([data/0]).
+
+-opaque data() :: {'data', term()}.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_user.erl b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_user.erl
new file mode 100644
index 0000000000..4e981f3b74
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/invalid_spec_2/scala_user.erl
@@ -0,0 +1,8 @@
+-module(scala_user).
+
+-export([is_list/2]).
+
+-spec is_list(atom(), scala_data:data()) -> boolean().
+
+is_list( List,Data) when is_list(List) -> true;
+is_list(Tuple,Data) when is_tuple(Tuple) -> false.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps_difftype.erl b/lib/dialyzer/test/small_SUITE_data/src/maps_difftype.erl
new file mode 100644
index 0000000000..19e61a7944
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/maps_difftype.erl
@@ -0,0 +1,11 @@
+%%
+%% File: maps_difftype.erl
+%% Author: Björn-Egil Dahlberg
+%% Created: 2014-04-29
+%%
+-module(maps_difftype).
+
+-export([empty_mismatch/1]).
+
+empty_mismatch(Tuple) when is_tuple(Tuple) ->
+ case Tuple of #{} -> ok end.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps_redef.erl b/lib/dialyzer/test/small_SUITE_data/src/maps_redef.erl
new file mode 100644
index 0000000000..70059f73b6
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/maps_redef.erl
@@ -0,0 +1,12 @@
+-module(maps_redef).
+
+-export([t/0]).
+
+%% OK in Erlang/OTP 17, at least.
+
+-type map() :: atom(). % redefine built-in type
+
+-spec t() -> map().
+
+t() ->
+ a. % OK
diff --git a/lib/dialyzer/test/small_SUITE_data/src/predef.erl b/lib/dialyzer/test/small_SUITE_data/src/predef.erl
index c2364fd1c2..ee9073aa67 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/predef.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/predef.erl
@@ -5,8 +5,8 @@
-export_type([array/0, digraph/0, gb_set/0]).
-%% Before R17B local re-definitions of pre-defined opaque types were
-%% ignored but did not generate any warning.
+%% Before Erlang/OTP 17.0 local re-definitions of pre-defined opaque
+%% types were ignored but did not generate any warning.
-opaque array() :: atom().
-opaque digraph() :: atom().
-opaque gb_set() :: atom().
diff --git a/lib/dialyzer/test/small_SUITE_data/src/remote_field.erl b/lib/dialyzer/test/small_SUITE_data/src/remote_field.erl
new file mode 100644
index 0000000000..c34fa1b9dd
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/remote_field.erl
@@ -0,0 +1,11 @@
+-module(remote_field).
+
+-type f(T) :: {ssl:sslsocket(), T}.
+
+-record(r1, { f1 :: f(_) }).
+-type r1(T) :: #r1{ f1 :: fun((ssl:sslsocket(), T) -> any()) }.
+
+-record(state, {
+ r :: r1(T),
+ arg :: T
+ }).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/remote_field2.erl b/lib/dialyzer/test/small_SUITE_data/src/remote_field2.erl
new file mode 100644
index 0000000000..35687e22ec
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/remote_field2.erl
@@ -0,0 +1,17 @@
+-module(remote_field2).
+
+-export([handle_cast/2]).
+
+-record(state, {tcp_socket :: inet:socket()}).
+
+-spec handle_cast(_,_) ->
+ {noreply,_} |
+ {stop,{shutdown,connection_closed},
+ #state{tcp_socket :: port()}}.
+handle_cast({send, Message}, #state{tcp_socket = TCPSocket} = State) ->
+ case gen_tcp:send(TCPSocket, Message) of
+ ok ->
+ {noreply, State};
+ {error, closed} ->
+ {stop, {shutdown, connection_closed}, State}
+ end.
diff --git a/lib/dialyzer/test/underspecs_SUITE_data/dialyzer_options b/lib/dialyzer/test/underspecs_SUITE_data/dialyzer_options
index f7197ac30f..6843119b9d 100644
--- a/lib/dialyzer/test/underspecs_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/underspecs_SUITE_data/dialyzer_options
@@ -1 +1 @@
-{dialyzer_options, [{warnings, [underspecs]}]}.
+{dialyzer_options, [{warnings, [underspecs, no_unknown]}]}.
diff --git a/lib/dialyzer/test/user_SUITE_data/dialyzer_options b/lib/dialyzer/test/user_SUITE_data/dialyzer_options
index 513ed7752b..d20ecd389f 100644
--- a/lib/dialyzer/test/user_SUITE_data/dialyzer_options
+++ b/lib/dialyzer/test/user_SUITE_data/dialyzer_options
@@ -1,2 +1,2 @@
-{dialyzer_options, []}.
+{dialyzer_options, [{warnings, [no_unknown]}]}.
{time_limit, 3}. \ No newline at end of file
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 7d6a28e51c..ab9ad25a3a 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -227,7 +227,7 @@ question is as if a callback had taken place and returned
<c>{error, failure}</c>.</p>
<p>
-Defaults to <c>report</c> if unspecified.</p>
+Defaults to <c>discard</c> if unspecified.</p>
</item>
<tag><c>{request_errors, answer_3xxx|answer|callback}</c></tag>
diff --git a/lib/diameter/doc/src/diameter_make.xml b/lib/diameter/doc/src/diameter_make.xml
index 13ec5bbfc1..0c7e6b794d 100644
--- a/lib/diameter/doc/src/diameter_make.xml
+++ b/lib/diameter/doc/src/diameter_make.xml
@@ -52,7 +52,7 @@ under the License.
<p>
The function &codec; is used to compile a diameter
&dictionary; into Erlang source.
-The resulting source implements the interface diameter required
+The resulting source implements the interface diameter requires
to encode and decode the dictionary's messages and AVPs.</p>
<p>
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index cd14195e78..68e69dbfeb 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -11,7 +11,7 @@
<header>
<copyright>
<year>2011</year>
-<year>2013</year>
+<year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -42,6 +42,164 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>diameter 1.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add missing check at dictionary compilation.</p>
+ <p>
+ In particular, that an AVP defined as having type Grouped
+ in an @avp_types section has a corresponding definition
+ in a @grouped section.</p>
+ <p>
+ Own Id: OTP-11561</p>
+ </item>
+ <item>
+ <p>
+ Correct documentation on the setting of Origin-State-Id</p>
+ <p>
+ It was incorrectly stated that the AVP would be set in an
+ outgoing DPR/DPA.</p>
+ <p>
+ Own Id: OTP-11583</p>
+ </item>
+ <item>
+ <p>
+ Change interface for communicating outbound stream id to
+ diameter_sctp</p>
+ <p>
+ The module uses the transport_data field of record
+ diameter_packet to communicate the stream on which the an
+ incoming message is received and on which an outgoing
+ message should be sent, the previous interface being that
+ both are communicated as a tuple of the form {stream,
+ Id}. However, since diameter retains the value of an
+ incoming request's transport_data unless the
+ corresponding answer message specifies otherwise, the
+ behaviour in this case is to send an answer on the
+ outbound stream with the same identifier as the that of
+ the inbound stream on which the request was received. If
+ the inbound stream id is greater than or equal to the
+ number of outbound streams then this is guaranteed to
+ fail, causing the transport process in question to
+ terminate. There is no relationship between inbound and
+ outbound stream identifiers so diameter_sctp's imposition
+ of one is simply wrong.</p>
+ <p>
+ Outbound stream ids are now communicated with a different
+ tuple: {outstream, Id}, interpreted modulo the number of
+ outbound streams. Thus, retention of an inbound request's
+ transport_data has no effect on the selection of an
+ outbound stream.</p>
+ <p>
+ The change in interface is not strictly backwards
+ compatible because of the new atom for the outbound
+ stream. However, as there is currently no documented way
+ of obtaining the available number of outbound streams for
+ a peer connection, there is no way for a client to have
+ known the range of ids from which it could reliably have
+ chosen with the previous interface, so any setting of the
+ outbound stream has probably been unintentional. Not
+ explicitly specifying an outbound stream now results in a
+ round-robin selection.</p>
+ <p>
+ Thanks to Sharmila Pillai for reporting the problem.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11593</p>
+ </item>
+ <item>
+ <p>
+ Fix unicode path failure in diameter_make:codec/2.</p>
+ <p>
+ A dictionary path containing a unicode codepoint > 255
+ caused the function to fail.</p>
+ <p>
+ Own Id: OTP-11655</p>
+ </item>
+ <item>
+ <p>
+ Fix 'accept' config to diameter_sctp.</p>
+ <p>
+ OTP-10893 added support for {accept, Match} tuples to
+ specify addresses or regexps that should be matched
+ against peer addresses to decide whether or not a newly
+ established association should be retained, but this
+ hasn't been functional in the SCTP case because of
+ missing support in inet(3).</p>
+ <p>
+ The display of both local and peer addresses in
+ diameter:service_info/2 output has also been corrected.</p>
+ <p>
+ Own Id: OTP-11661 Aux Id: OTP-10229 </p>
+ </item>
+ <item>
+ <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
+ not have the 'M' (mandatory) bit set and one or more of
+ the encapsulated AVPs within the group has the 'M'
+ (mandatory) bit set MAY simply be ignored if the Grouped
+ AVP itself is unrecognized. The rule applies even if the
+ encapsulated AVP with its 'M' (mandatory) bit set is
+ further encapsulated within other sub-groups, i.e., other
+ Grouped AVPs embedded within the Grouped
+ AVP.</c></p></item></taglist></p>
+ <p>
+ The first sentence is mangled but take it to mean this:</p>
+ <p>
+ <taglist><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>
+ <p>
+ This is a bit of a non-statement since if the AVP is
+ unrecognized then its type is unknown. We therefore don't
+ know that its data bytes contain encapsulated AVPs, so
+ can't but ignore any of those that set the M-bit. Doing
+ anything else when the type *is* known would be
+ inconsistent.</p>
+ <p>
+ OTP-11087 (R16B03) caused the M-bit on any unrecognized
+ AVP to be regarded as an error, unrecognized being taken
+ to mean "not explicitly defined as a member of its
+ container". (That is, an AVP that can't be packed into a
+ dedicated record field, which is slightly stronger than
+ "not defined".) This fixed the original intention for
+ top-level AVPs but broke the required leniency for
+ Grouped AVPs whose type is known. This leniency is now
+ restored.</p>
+ <p>
+ Note that dictionary files need to be recompiled for the
+ change to have effect.</p>
+ <p>
+ Thanks to Rory McKeown for reporting the problem.</p>
+ <p>
+ Own Id: OTP-11675 Aux Id: OTP-11087 </p>
+ </item>
+ <item>
+ <p>
+ Fix pick_peer case clause failure.</p>
+ <p>
+ In the case of {call_mutates_state, true} configuration
+ on the service in question, any peer selection that
+ failed to select a peer resulted in a case clause
+ failure. This was noticed in the case of a peer failover
+ in which an alternate peer wasn't available.</p>
+ <p>
+ Own Id: OTP-11789</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 1.5</title>
<section><title>Improvements and New Features</title>
@@ -73,12 +231,6 @@ first.</p>
<p>
Own Id: OTP-11361</p>
</item>
- <item>
- <p>
- Fix silent make rules (Thanks to Anthony Ramine)</p>
- <p>
- Own Id: OTP-11514</p>
- </item>
</list>
</section>
@@ -86,7 +238,7 @@ first.</p>
<section><title>diameter 1.4.4</title>
- <section><title>Known Bugs and Problems</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
<p>
diff --git a/lib/diameter/examples/code/client.erl b/lib/diameter/examples/code/client.erl
index bfe71b0e56..46eb4a55db 100644
--- a/lib/diameter/examples/code/client.erl
+++ b/lib/diameter/examples/code/client.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/diameter/examples/code/client_cb.erl b/lib/diameter/examples/code/client_cb.erl
index ee3dcb2fec..843cdd9262 100644
--- a/lib/diameter/examples/code/client_cb.erl
+++ b/lib/diameter/examples/code/client_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -77,23 +77,11 @@ prepare_retransmit(Packet, SvcName, Peer) ->
%% handle_answer/4
-%% Since client.erl has detached the call when using the list
-%% encoding and not otherwise, output to the terminal in the
-%% the former case, return in the latter.
-
-handle_answer(#diameter_packet{msg = Msg}, Request, _SvcName, _Peer)
- when is_list(Request) ->
- io:format("answer: ~p~n", [Msg]);
-
handle_answer(#diameter_packet{msg = Msg}, _Request, _SvcName, _Peer) ->
{ok, Msg}.
%% handle_error/4
-handle_error(Reason, Request, _SvcName, _Peer)
- when is_list(Request) ->
- io:format("error: ~p~n", [Reason]);
-
handle_error(Reason, _Request, _SvcName, _Peer) ->
{error, Reason}.
diff --git a/lib/diameter/examples/code/redirect_cb.erl b/lib/diameter/examples/code/redirect_cb.erl
index 69836774a1..8d98b0d2df 100644
--- a/lib/diameter/examples/code/redirect_cb.erl
+++ b/lib/diameter/examples/code/redirect_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,12 +34,10 @@
-define(UNEXPECTED, erlang:error({unexpected, ?MODULE, ?LINE})).
-peer_up(_SvcName, {PeerRef, _}, State) ->
- io:format("up: ~p~n", [PeerRef]),
+peer_up(_SvcName, _Peer, State) ->
State.
-peer_down(_SvcName, {PeerRef, _}, State) ->
- io:format("down: ~p~n", [PeerRef]),
+peer_down(_SvcName, _Peer, State) ->
State.
pick_peer(_, _, _SvcName, _State) ->
diff --git a/lib/diameter/examples/code/relay_cb.erl b/lib/diameter/examples/code/relay_cb.erl
index 9f9cd8d5ae..68798014e6 100644
--- a/lib/diameter/examples/code/relay_cb.erl
+++ b/lib/diameter/examples/code/relay_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,12 +32,10 @@
handle_error/5,
handle_request/3]).
-peer_up(_SvcName, {PeerRef, _}, State) ->
- io:format("up: ~p~n", [PeerRef]),
+peer_up(_SvcName, _Peer, State) ->
State.
-peer_down(_SvcName, {PeerRef, _}, State) ->
- io:format("down: ~p~n", [PeerRef]),
+peer_down(_SvcName, _Peer, State) ->
State.
%% Returning 'relay' from handle_request causes diameter to resend the
diff --git a/lib/diameter/examples/code/server_cb.erl b/lib/diameter/examples/code/server_cb.erl
index 0f6eb32ed6..9d8d395d06 100644
--- a/lib/diameter/examples/code/server_cb.erl
+++ b/lib/diameter/examples/code/server_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,12 +38,10 @@
-define(UNEXPECTED, erlang:error({unexpected, ?MODULE, ?LINE})).
-peer_up(_SvcName, {PeerRef, _}, State) ->
- io:format("up: ~p~n", [PeerRef]),
+peer_up(_SvcName, _Peer, State) ->
State.
-peer_down(_SvcName, {PeerRef, _}, State) ->
- io:format("down: ~p~n", [PeerRef]),
+peer_down(_SvcName, _Peer, State) ->
State.
pick_peer(_, _, _SvcName, _State) ->
@@ -68,10 +66,13 @@ handle_request(#diameter_packet{msg = Req, errors = []}, _SvcName, {_, Caps})
origin_realm = {OR,_}}
= Caps,
#diameter_base_RAR{'Session-Id' = Id,
- 'Re-Auth-Request-Type' = RT}
+ 'Re-Auth-Request-Type' = Type}
= Req,
- {reply, answer(RT, Id, OH, OR)};
+ {reply, #diameter_base_RAA{'Result-Code' = rc(Type),
+ 'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Session-Id' = Id}};
%% ... or one that wasn't. 3xxx errors are answered by diameter itself
%% but these are 5xxx errors for which we must contruct a reply.
@@ -84,32 +85,18 @@ handle_request(#diameter_packet{msg = Req}, _SvcName, {_, Caps})
#diameter_base_RAR{'Session-Id' = Id}
= Req,
- Ans = #diameter_base_RAA{'Origin-Host' = OH,
- 'Origin-Realm' = OR,
- 'Session-Id' = Id},
+ {reply, #diameter_base_RAA{'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Session-Id' = Id}};
- {reply, Ans};
+%% Answer that any other message is unsupported.
+handle_request(#diameter_packet{}, _SvcName, _) ->
+ {answer_message, 3001}. %% DIAMETER_COMMAND_UNSUPPORTED
-%% Should really reply to other base messages that we don't support
-%% but simply discard them instead.
-handle_request(#diameter_packet{}, _SvcName, {_,_}) ->
- discard.
+%% Map Re-Auth-Request-Type to Result-Code just for the purpose of
+%% generating different answers.
-%% ---------------------------------------------------------------------------
-
-%% Answer using the record or list encoding depending on
-%% Re-Auth-Request-Type. This is just as an example. You would
-%% typically just choose one, and this has nothing to do with the how
-%% client.erl sends.
-
-answer(0, Id, OH, OR) ->
- #diameter_base_RAA{'Result-Code' = 2001, %% DIAMETER_SUCCESS
- 'Origin-Host' = OH,
- 'Origin-Realm' = OR,
- 'Session-Id' = Id};
-
-answer(_, Id, OH, OR) ->
- ['RAA', {'Result-Code', 5012}, %% DIAMETER_UNABLE_TO_COMPLY
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Session-Id', Id}].
+rc(0) ->
+ 2001; %% DIAMETER_SUCCESS
+rc(_) ->
+ 5012. %% DIAMETER_UNABLE_TO_COMPLY
diff --git a/lib/diameter/include/diameter.hrl b/lib/diameter/include/diameter.hrl
index 79c4dce541..c2c271a9a3 100644
--- a/lib/diameter/include/diameter.hrl
+++ b/lib/diameter/include/diameter.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -58,8 +58,8 @@
-record(diameter_header,
{version, %% 8-bit unsigned
length, %% 24-bit unsigned
- cmd_code, %% 8-bit unsigned
- application_id, %% 24-bit unsigned
+ cmd_code, %% 24-bit unsigned
+ application_id, %% 32-bit unsigned
hop_by_hop_id, %% 32-bit unsigned
end_to_end_id, %% 32-bit unsigned
is_request, %% boolean() R flag
@@ -126,7 +126,7 @@
default,
extra = []}).
-%% The diameter service and diameter_apps records are only passed
+%% The diameter service and diameter_app records are only passed
%% through the transport interface when starting a transport process,
%% although typically a transport implementation will (and probably
%% should) only be interested host_ip_address.
@@ -143,6 +143,7 @@
init_state, %% option 'state', initial callback state
id, %% 32-bit unsigned application identifier = Dict:id()
mutable = false, %% boolean(), do traffic callbacks modify state?
- options = [{answer_errors, report}, %% | callback | discard
+ options = [{answer_errors, discard}, %% | callback | report
{request_errors, answer_3xxx}]}). %% | callback | answer
+
-endif. %% -ifdef(diameter_hrl).
diff --git a/lib/diameter/include/diameter_gen.hrl b/lib/diameter/include/diameter_gen.hrl
index c8f706dc3e..7e91ce375f 100644
--- a/lib/diameter/include/diameter_gen.hrl
+++ b/lib/diameter/include/diameter_gen.hrl
@@ -30,6 +30,10 @@
%% error or not. See is_strict/0.
-define(STRICT_KEY, strict).
+%% Key that says whether or not we should do a best-effort decode
+%% within Failed-AVP.
+-define(FAILED_KEY, failed).
+
-type parent_name() :: atom(). %% parent = Message or AVP
-type parent_record() :: tuple(). %%
-type avp_name() :: atom().
@@ -286,15 +290,7 @@ decode(Name, 'AVP', Avp, Acc) ->
%% d/3
-%% Don't try to decode the value of a Failed-AVP component since it
-%% probably won't. Note that matching on 'Failed-AVP' assumes that
-%% this is the RFC AVP, with code 279. Strictly, this doesn't need to
-%% be the case, so we're assuming no one defines another Failed-AVP.
-d('Failed-AVP' = Name, Avp, Acc) ->
- decode_AVP(Name, Avp, Acc);
-
-%% Or try to decode.
-d(Name, Avp, {Avps, Acc}) ->
+d(Name, Avp, Acc) ->
#diameter_avp{name = AvpName,
data = Data,
type = Type,
@@ -307,51 +303,81 @@ d(Name, Avp, {Avps, Acc}) ->
%% value around through the entire decode. The solution here is
%% simple in comparison, both to implement and to understand.
- Reset = relax(Type, M),
+ Strict = relax(Type, M),
+ %% Use the process dictionary again to keep track of whether we're
+ %% decoding within Failed-AVP and should ignore decode errors
+ %% altogether.
+
+ Failed = relax(Name), %% Not AvpName or else a failed Failed-AVP
+ %% decode is packed into 'AVP'.
try avp(decode, Data, AvpName) of
V ->
+ {Avps, T} = Acc,
{H, A} = ungroup(V, Avp),
- {[H | Avps], pack_avp(Name, A, Acc)}
+ {[H | Avps], pack_avp(Name, A, T)}
catch
error: Reason ->
- %% Failures here won't be visible since they're a "normal"
- %% occurrence if the peer sends a faulty AVP that we need to
- %% respond sensibly to. Log the occurence for traceability,
- %% but the peer will also receive info in the resulting
- %% answer-message.
- diameter_lib:log({decode, failure},
- ?MODULE,
- ?LINE,
- {Reason, Avp, erlang:get_stacktrace()}),
- {Rec, Failed} = Acc,
- {[Avp|Avps], {Rec, [rc(Reason, Avp) | Failed]}}
+ d(undefined == Failed orelse is_failed(), Reason, Name, Avp, Acc)
after
- relax(Reset)
+ reset(?STRICT_KEY, Strict),
+ reset(?FAILED_KEY, Failed)
end.
+%% Ignore a decode error within Failed-AVP ...
+d(true, _, Name, Avp, Acc) ->
+ decode_AVP(Name, Avp, Acc);
+
+%% ... or not. Failures here won't be visible since they're a "normal"
+%% occurrence if the peer sends a faulty AVP that we need to respond
+%% sensibly to. Log the occurence for traceability, but the peer will
+%% also receive info in the resulting answer message.
+d(false, Reason, Name, Avp, {Avps, Acc}) ->
+ Stack = diameter_lib:get_stacktrace(),
+ diameter_lib:log(decode_error,
+ ?MODULE,
+ ?LINE,
+ {Reason, Name, Avp#diameter_avp.name, Stack}),
+ {Rec, Failed} = Acc,
+ {[Avp|Avps], {Rec, [rc(Reason, Avp) | Failed]}}.
+
%% Set false in the process dictionary as soon as we see a Grouped AVP
%% that doesn't set the M-bit, so that is_strict() can say whether or
%% not to ignore the M-bit on an encapsulated AVP.
relax('Grouped', M) ->
- V = getr(?STRICT_KEY),
- if V == undefined andalso not M ->
+ case getr(?STRICT_KEY) of
+ undefined when not M ->
putr(?STRICT_KEY, M);
- true ->
+ _ ->
false
end;
relax(_, _) ->
false.
-%% Reset strictness.
-relax(undefined) ->
- eraser(?STRICT_KEY);
-relax(false) ->
- ok.
-
is_strict() ->
false /= getr(?STRICT_KEY).
+%% Set true in the process dictionary as soon as we see Failed-AVP.
+%% Matching on 'Failed-AVP' assumes that this is the RFC AVP.
+%% Strictly, this doesn't need to be the case.
+relax('Failed-AVP') ->
+ case getr(?FAILED_KEY) of
+ undefined ->
+ putr(?FAILED_KEY, true);
+ true = Yes ->
+ Yes
+ end;
+relax(_) ->
+ is_failed().
+
+is_failed() ->
+ true == getr(?FAILED_KEY).
+
+reset(Key, undefined) ->
+ eraser(Key);
+reset(_, _) ->
+ ok.
+
%% decode_AVP/3
%%
%% Don't know this AVP: see if it can be packed in an 'AVP' field
@@ -410,6 +436,23 @@ pack_avp(_, Arity, Avp, Acc) ->
%% pack_AVP/3
+%% Length failure was induced because of a header/payload length
+%% mismatch. The AVP Length is reset to match the received data if
+%% this AVP is encoded in an answer message, since the length is
+%% computed.
+%%
+%% Data is a truncated header if command_code = undefined, otherwise
+%% payload bytes. The former is padded to the length of a header if
+%% the AVP reaches an outgoing encode in diameter_codec.
+%%
+%% RFC 6733 says that an AVP returned with 5014 can contain a minimal
+%% payload for the AVP's type, but in this case we don't know the
+%% type.
+
+pack_AVP(_, #diameter_avp{data = <<0:1, Data/binary>>} = Avp, Acc) ->
+ {Rec, Failed} = Acc,
+ {Rec, [{5014, Avp#diameter_avp{data = Data}} | Failed]};
+
pack_AVP(Name, #diameter_avp{is_mandatory = M} = Avp, Acc) ->
case pack_arity(Name, M) of
0 ->
@@ -422,7 +465,15 @@ pack_AVP(Name, #diameter_avp{is_mandatory = M} = Avp, Acc) ->
%% Give Failed-AVP special treatment since it'll contain any
%% unrecognized mandatory AVP's.
pack_arity(Name, M) ->
- case Name /= 'Failed-AVP' andalso M andalso is_strict() of
+ NF = Name /= 'Failed-AVP' andalso not is_failed(),
+ %% Not testing just Name /= 'Failed-AVP' means we're changing the
+ %% packing of AVPs nested within Failed-AVP, but the point of
+ %% ignoring errors within Failed-AVP is to decode as much as
+ %% possible, and failing because a mandatory AVP couldn't be
+ %% packed into a dedicated field defeats that point. Note that we
+ %% can't just test not is_failed() since this will be 'true' when
+ %% packing an unknown AVP directly within Failed-AVP.
+ case NF andalso M andalso is_strict() of
true ->
0;
false ->
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 578bbaee2e..9afccf298c 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2013. All Rights Reserved.
+# Copyright Ericsson AB 2010-2014. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -41,7 +41,7 @@ INCDIR = ../include
ABS_EBIN := $(shell cd $(EBIN) && pwd)
# Where make should look for dependencies.
-VPATH = .:base:compiler:transport:gen
+VPATH = .:base:compiler:transport:gen:info
# ----------------------------------------------------
# Target specs
@@ -55,13 +55,13 @@ DICT_ERLS = $(DICT_MODULES:%=%.erl)
DICT_HRLS = $(DICT_MODULES:%=%.hrl)
# Modules to build before compiling dictionaries.
-COMPILER_MODULES = $(notdir $(filter compiler/%, $(CT_MODULES))) \
- $(DICT_YRL)
+COMPILER_MODULES = $(notdir $(CT_MODULES)) $(DICT_YRL)
# All handwritten modules from which a depend.mk is generated.
MODULES = \
$(RT_MODULES) \
- $(CT_MODULES)
+ $(CT_MODULES) \
+ $(INFO_MODULES)
# Modules whose names are inserted into the app file.
APP_MODULES = \
@@ -72,6 +72,7 @@ APP_MODULES = \
TARGET_MODULES = \
$(APP_MODULES) \
$(CT_MODULES) \
+ $(INFO_MODULES) \
$(DICT_YRL:%=gen/%)
# What to build for the 'opt' target.
@@ -147,14 +148,19 @@ gen/$(DICT_YRL).erl: compiler/$(DICT_YRL).yrl
$(ERLC) -Werror -o $(@D) $<
# Generate the app file.
-$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk
+$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk app.sed
$(gen_verbose) \
M=`echo $(notdir $(APP_MODULES)) | tr ' ' ,`; \
+ C=`echo $(COMPILER_MODULES) | tr ' ' ,`; \
+ I=`echo $(notdir $(INFO_MODULES)) | tr ' ' ,`; \
R=`echo $(REGISTERED) | tr ' ' ,`; \
sed -e 's;%VSN%;$(VSN);' \
-e "s;%MODULES%;$$M;" \
+ -e "s;%COMPILER%;$$C;" \
+ -e "s;%INFO%;$$I;" \
-e "s;%REGISTERED%;$$R;" \
- $< > $@
+ $< \
+ | sed -f app.sed > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
$(vsn_verbose) \
@@ -177,6 +183,8 @@ info:
@echo
@$(call list,CT_MODULES)
@echo
+ @$(call list,INFO_MODULES)
+ @echo
@$(call list,TARGET_MODULES)
@echo
@$(call list,TARGET_DIRS)
@@ -216,7 +224,7 @@ dialyze: opt $(PLT)
-Wno_improper_lists \
$(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR) \
$(patsubst %, $(EBIN)/%.$(EMULATOR), \
- $(notdir $(RT_MODULES) $(CT_MODULES)))
+ $(notdir $(RT_MODULES) $(CT_MODULES) $(INFO_MODULES)))
# Omit all but the common dictionary module since these
# (diameter_gen_relay in particular) generate warning depending on how
# much of the included diameter_gen.hrl they use.
diff --git a/lib/diameter/src/app.sed b/lib/diameter/src/app.sed
new file mode 100644
index 0000000000..7916f65002
--- /dev/null
+++ b/lib/diameter/src/app.sed
@@ -0,0 +1,40 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2014. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+#
+# Generate runtime_dependencies from applications to avoid having to
+# specify the same application more than once.
+#
+
+/{runtime_dependencies,/b v
+/{[-a-z]*, "[0-9.]*"}/!b
+/{vsn,/b
+
+/%%/!H
+s/{\([^,]*\)[^}]*}/\1/g
+s/%%/%,/
+b
+
+:v
+
+p
+x
+s/\n//
+s/%//g
+s/\n */ /g
+s/{\([^,]*\), "\([^"]*"\)}/"\1-\2/g
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index 0de4d53973..06a4f5de64 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -70,12 +70,15 @@ encode(Mod, #diameter_packet{} = Pkt) ->
try
e(Mod, Pkt)
catch
+ exit: {Reason, Stack, #diameter_header{} = H} = T ->
+ %% Exit with a header in the reason to let the caller
+ %% count encode errors.
+ ?LOG(encode_error, {Reason, Stack, H}),
+ exit({?MODULE, encode, T});
error: Reason ->
- %% Be verbose since a crash report may be truncated and
- %% encode errors are self-inflicted.
- X = {?MODULE, encode, {Reason, ?STACK}},
- diameter_lib:error_report(X, {?MODULE, encode, [Mod, Pkt]}),
- exit(X)
+ T = {Reason, diameter_lib:get_stacktrace()},
+ ?LOG(encode_error, T),
+ exit({?MODULE, encode, T})
end;
encode(Mod, Msg) ->
@@ -87,53 +90,62 @@ encode(Mod, Msg) ->
msg = Msg}).
e(_, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} = Pkt) ->
- Avps = encode_avps(As),
- Length = size(Avps) + 20,
-
- #diameter_header{version = Vsn,
- cmd_code = Code,
- application_id = Aid,
- hop_by_hop_id = Hid,
- end_to_end_id = Eid}
- = Hdr,
-
- Flags = make_flags(0, Hdr),
-
- Pkt#diameter_packet{header = Hdr,
- bin = <<Vsn:8, Length:24,
- Flags:8, Code:24,
- Aid:32,
- Hid:32,
- Eid:32,
- Avps/binary>>};
+ try encode_avps(As) of
+ Avps ->
+ Length = size(Avps) + 20,
+
+ #diameter_header{version = Vsn,
+ cmd_code = Code,
+ application_id = Aid,
+ hop_by_hop_id = Hid,
+ end_to_end_id = Eid}
+ = Hdr,
+
+ Flags = make_flags(0, Hdr),
+
+ Pkt#diameter_packet{header = Hdr,
+ bin = <<Vsn:8, Length:24,
+ Flags:8, Code:24,
+ Aid:32,
+ Hid:32,
+ Eid:32,
+ Avps/binary>>}
+ catch
+ error: Reason ->
+ exit({Reason, diameter_lib:get_stacktrace(), Hdr})
+ end;
-e(Mod, #diameter_packet{header = Hdr, msg = Msg} = Pkt) ->
+e(Mod, #diameter_packet{header = Hdr0, msg = Msg} = Pkt) ->
#diameter_header{version = Vsn,
hop_by_hop_id = Hid,
end_to_end_id = Eid}
- = Hdr,
+ = Hdr0,
MsgName = rec2msg(Mod, Msg),
- {Code, Flags0, Aid} = msg_header(Mod, MsgName, Hdr),
- Flags = make_flags(Flags0, Hdr),
-
- Avps = encode_avps(Mod, MsgName, values(Msg)),
- Length = size(Avps) + 20,
-
- Pkt#diameter_packet{header = Hdr#diameter_header
- {length = Length,
- cmd_code = Code,
- application_id = Aid,
- is_request = 0 /= ?MASK(7, Flags),
- is_proxiable = 0 /= ?MASK(6, Flags),
- is_error = 0 /= ?MASK(5, Flags),
- is_retransmitted = 0 /= ?MASK(4, Flags)},
- bin = <<Vsn:8, Length:24,
- Flags:8, Code:24,
- Aid:32,
- Hid:32,
- Eid:32,
- Avps/binary>>}.
+ {Code, Flags0, Aid} = msg_header(Mod, MsgName, Hdr0),
+ Flags = make_flags(Flags0, Hdr0),
+ Hdr = Hdr0#diameter_header{cmd_code = Code,
+ application_id = Aid,
+ is_request = 0 /= ?MASK(7, Flags),
+ is_proxiable = 0 /= ?MASK(6, Flags),
+ is_error = 0 /= ?MASK(5, Flags),
+ is_retransmitted = 0 /= ?MASK(4, Flags)},
+ Values = values(Msg),
+
+ try encode_avps(Mod, MsgName, Values) of
+ Avps ->
+ Length = size(Avps) + 20,
+ Pkt#diameter_packet{header = Hdr#diameter_header{length = Length},
+ bin = <<Vsn:8, Length:24,
+ Flags:8, Code:24,
+ Aid:32,
+ Hid:32,
+ Eid:32,
+ Avps/binary>>}
+ catch
+ error: Reason ->
+ exit({Reason, diameter_lib:get_stacktrace(), Hdr})
+ end.
%% make_flags/2
@@ -225,7 +237,7 @@ rec2msg(Mod, Rec) ->
%% Unsuccessfully decoded AVPs will be placed in #diameter_packet.errors.
--spec decode(module(), #diameter_packet{} | bitstring())
+-spec decode(module(), #diameter_packet{} | binary())
-> #diameter_packet{}.
decode(Mod, Pkt) ->
@@ -259,34 +271,34 @@ decode(_, Mod, #diameter_packet{header = Hdr} = Pkt) ->
decode_avps(MsgName, Mod, Pkt, collect_avps(Pkt));
decode(Id, Mod, Bin)
- when is_bitstring(Bin) ->
+ when is_binary(Bin) ->
decode(Id, Mod, #diameter_packet{header = decode_header(Bin), bin = Bin}).
decode_avps(MsgName, Mod, Pkt, {E, Avps}) ->
- ?LOG(invalid, Pkt#diameter_packet.bin),
+ ?LOG(invalid_avp_length, Pkt#diameter_packet.header),
#diameter_packet{errors = Failed}
= P
= decode_avps(MsgName, Mod, Pkt, Avps),
P#diameter_packet{errors = [E | Failed]};
-decode_avps('', Mod, Pkt, Avps) -> %% unknown message ...
- ?LOG(unknown, {Mod, Pkt#diameter_packet.header}),
+decode_avps('', _, Pkt, Avps) -> %% unknown message ...
+ ?LOG(unknown_message, Pkt#diameter_packet.header),
Pkt#diameter_packet{avps = lists:reverse(Avps),
errors = [3001]}; %% DIAMETER_COMMAND_UNSUPPORTED
%% msg = undefined identifies this case.
decode_avps(MsgName, Mod, Pkt, Avps) -> %% ... or not
- {Rec, As, Failed} = Mod:decode_avps(MsgName, Avps),
- ?LOGC([] /= Failed, failed, {Mod, Failed}),
+ {Rec, As, Errors} = Mod:decode_avps(MsgName, Avps),
+ ?LOGC([] /= Errors, decode_errors, Pkt#diameter_packet.header),
Pkt#diameter_packet{msg = Rec,
- errors = Failed,
+ errors = Errors,
avps = As}.
%%% ---------------------------------------------------------------------------
%%% # decode_header/1
%%% ---------------------------------------------------------------------------
--spec decode_header(bitstring())
+-spec decode_header(binary())
-> #diameter_header{}
| false.
@@ -297,7 +309,7 @@ decode_header(<<Version:8,
ApplicationId:32,
HopByHopId:32,
EndToEndId:32,
- _/bitstring>>) ->
+ _/binary>>) ->
<<R:1, P:1, E:1, T:1, _:4>>
= CmdFlags,
%% 3588 (ch 3) says that reserved bits MUST be set to 0 and ignored
@@ -410,7 +422,7 @@ msg_id(#diameter_header{application_id = A,
is_request = R}) ->
{A, C, if R -> 1; true -> 0 end};
-msg_id(<<_:32, Rbit:1, _:7, CmdCode:24, ApplId:32, _/bitstring>>) ->
+msg_id(<<_:32, Rbit:1, _:7, CmdCode:24, ApplId:32, _/binary>>) ->
{ApplId, CmdCode, Rbit}.
%%% ---------------------------------------------------------------------------
@@ -421,17 +433,18 @@ msg_id(<<_:32, Rbit:1, _:7, CmdCode:24, ApplId:32, _/bitstring>>) ->
%% order in the binary. Note also that grouped avp's aren't unraveled,
%% only those at the top level.
--spec collect_avps(#diameter_packet{} | bitstring())
+-spec collect_avps(#diameter_packet{} | binary())
-> [Avp]
| {Error, [Avp]}
when Avp :: #diameter_avp{},
Error :: {5014, #diameter_avp{}}.
collect_avps(#diameter_packet{bin = Bin}) ->
- <<_:20/binary, Avps/bitstring>> = Bin,
+ <<_:20/binary, Avps/binary>> = Bin,
collect_avps(Avps);
-collect_avps(Bin) ->
+collect_avps(Bin)
+ when is_binary(Bin) ->
collect_avps(Bin, 0, []).
collect_avps(<<>>, _, Acc) ->
@@ -461,7 +474,9 @@ collect_avps(Bin, N, Acc) ->
split_avp(Bin) ->
{Code, V, M, P, Len, HdrLen} = split_head(Bin),
- {Data, B} = split_data(Bin, HdrLen, Len - HdrLen),
+
+ <<_:HdrLen/binary, Rest/binary>> = Bin,
+ {Data, B} = split_data(Rest, Len - HdrLen),
{B, #diameter_avp{code = Code,
vendor_id = V,
@@ -471,17 +486,15 @@ split_avp(Bin) ->
%% split_head/1
-split_head(<<Code:32, 1:1, M:1, P:1, _:5, Len:24, V:32, _/bitstring>>) ->
+split_head(<<Code:32, 1:1, M:1, P:1, _:5, Len:24, V:32, _/binary>>) ->
{Code, V, M, P, Len, 12};
-split_head(<<Code:32, 0:1, M:1, P:1, _:5, Len:24, _/bitstring>>) ->
+split_head(<<Code:32, 0:1, M:1, P:1, _:5, Len:24, _/binary>>) ->
{Code, undefined, M, P, Len, 8};
-%% Header is truncated: pack_avp/1 will pad to the minimum header
-%% length.
-split_head(B)
- when is_bitstring(B) ->
- ?THROW({5014, #diameter_avp{data = B}}).
+%% Header is truncated.
+split_head(Bin) ->
+ ?THROW({5014, #diameter_avp{data = Bin}}).
%% 3588:
%%
@@ -516,34 +529,27 @@ split_head(B)
%% split_data/3
-split_data(Bin, HdrLen, Len)
- when 0 =< Len ->
- split_data(Bin, HdrLen, Len, (4 - (Len rem 4)) rem 4);
+split_data(Bin, Len) ->
+ Pad = (4 - (Len rem 4)) rem 4,
-split_data(_, _, _) ->
- invalid_avp_length().
+ %% Len might be negative here, but that ensures the failure of the
+ %% binary match.
-%% split_data/4
-
-split_data(Bin, HdrLen, Len, Pad) ->
case Bin of
- <<_:HdrLen/binary, Data:Len/binary, _:Pad/binary, Rest/bitstring>> ->
+ <<Data:Len/binary, _:Pad/binary, Rest/binary>> ->
{Data, Rest};
_ ->
- invalid_avp_length()
+ %% Header length points past the end of the message. As
+ %% stated in the 6733 text above, it's sufficient to
+ %% return a zero-filled minimal payload if this is a
+ %% request. Do this (in cases that we know the type) by
+ %% inducing a decode failure and letting the dictionary's
+ %% decode (in diameter_gen) deal with it. Here we don't
+ %% know type. If the type isn't known, then the decode
+ %% just strips the extra bit.
+ {<<0:1, Bin/binary>>, <<>>}
end.
-%% invalid_avp_length/0
-%%
-%% AVP Length doesn't mesh with payload. Induce a decode error by
-%% returning a payload that no valid Diameter type can have. This is
-%% so that a known AVP will result in 5014 error with a zero'd
-%% payload. Here we simply don't know how to construct this payload.
-%% (Yes, this solution is an afterthought.)
-
-invalid_avp_length() ->
- {<<0:1>>, <<>>}.
-
%%% ---------------------------------------------------------------------------
%%% # pack_avp/1
%%% ---------------------------------------------------------------------------
@@ -575,17 +581,23 @@ pack_avp(#diameter_avp{data = {Dict, Name, Value}} = A) ->
{Name, Type} = Dict:avp_name(Code, Vid),
pack_avp(A#diameter_avp{data = {Hdr, {Type, Value}}});
+%% ... with a truncated header ...
pack_avp(#diameter_avp{code = undefined, data = B})
- when is_bitstring(B) ->
+ when is_binary(B) ->
%% Reset the AVP Length of an AVP Header resulting from a 5014
%% error. The RFC doesn't explicitly say to do this but the
%% receiver can't correctly extract this and following AVP's
%% without a correct length. On the downside, the header doesn't
%% reveal if the received header has been padded.
Pad = 8*header_length(B) - bit_size(B),
- Len = size(<<H:5/binary, _:24, T/binary>> = <<B/bitstring, 0:Pad>>),
+ Len = size(<<H:5/binary, _:24, T/binary>> = <<B/binary, 0:Pad>>),
<<H/binary, Len:24, T/binary>>;
+%% ... from a dictionary compiled against old code in diameter_gen ...
+%% ... when ignoring errors in Failed-AVP ...
+pack_avp(#diameter_avp{data = <<0:1, B/binary>>} = A) ->
+ pack_avp(A#diameter_avp{data = B});
+
%% ... or as an iolist.
pack_avp(#diameter_avp{code = Code,
vendor_id = V,
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index f5ea459fd0..dd1c9b73bb 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-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -753,7 +753,7 @@ app_acc({application, Opts} = T, Acc) ->
Alias = get_opt(alias, Opts, Dict),
ModS = get_opt(state, Opts, Alias),
M = get_opt(call_mutates_state, Opts, false, [true]),
- A = get_opt(answer_errors, Opts, report, [callback, discard]),
+ A = get_opt(answer_errors, Opts, discard, [callback, report]),
P = get_opt(request_errors, Opts, answer_3xxx, [answer, callback]),
[#diameter_app{alias = Alias,
dictionary = Dict,
diff --git a/lib/diameter/src/base/diameter_lib.erl b/lib/diameter/src/base/diameter_lib.erl
index 44d81e2778..5b3a2063f8 100644
--- a/lib/diameter/src/base/diameter_lib.erl
+++ b/lib/diameter/src/base/diameter_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,6 +25,8 @@
now_diff/1,
time/1,
eval/1,
+ eval_name/1,
+ get_stacktrace/0,
ipaddr/1,
spawn_opts/2,
wait/1,
@@ -32,6 +34,22 @@
log/4]).
%% ---------------------------------------------------------------------------
+%% # get_stacktrace/0
+%% ---------------------------------------------------------------------------
+
+%% Return a stacktrace with a leading, potentially large, argument
+%% list replaced by an arity. Trace on stacktrace/0 to see the
+%% original.
+
+get_stacktrace() ->
+ stacktrace(erlang:get_stacktrace()).
+
+stacktrace([{M,F,A,L} | T]) when is_list(A) ->
+ [{M, F, length(A), L} | T];
+stacktrace(L) ->
+ L.
+
+%% ---------------------------------------------------------------------------
%% # info_report/2
%% ---------------------------------------------------------------------------
@@ -60,9 +78,17 @@ warning_report(Reason, T) ->
report(fun error_logger:warning_report/1, Reason, T).
report(Fun, Reason, T) ->
- Fun([{why, Reason}, {who, self()}, {what, T}]),
+ Fun(io_lib:format("diameter: ~" ++ fmt(Reason) ++ "~n ~p~n",
+ [Reason, T])),
false.
+fmt(T) ->
+ if is_list(T) ->
+ "s";
+ true ->
+ "p"
+ end.
+
%% ---------------------------------------------------------------------------
%% # now_diff/1
%% ---------------------------------------------------------------------------
@@ -129,8 +155,8 @@ eval({M,F,A}) ->
eval([{M,F,A} | X]) ->
apply(M, F, X ++ A);
-eval([[F|A] | X]) ->
- eval([F | X ++ A]);
+eval([[F|X] | A]) ->
+ eval([F | A ++ X]);
eval([F|A]) ->
apply(F,A);
@@ -142,6 +168,28 @@ eval(F) ->
F().
%% ---------------------------------------------------------------------------
+%% eval_name/1
+%% ---------------------------------------------------------------------------
+
+eval_name({M,F,A}) ->
+ {M, F, length(A)};
+
+eval_name([{M,F,A} | X]) ->
+ {M, F, length(A) + length(X)};
+
+eval_name([[F|A] | X]) ->
+ eval_name([F | X ++ A]);
+
+eval_name([F|_]) ->
+ F;
+
+eval_name({F}) ->
+ eval_name(F);
+
+eval_name(F) ->
+ F.
+
+%% ---------------------------------------------------------------------------
%% # ipaddr/1
%%
%% Parse an IP address.
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index f76bd96c3c..31e570ae20 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -283,7 +283,7 @@ handle_info(T, #state{} = State) ->
ok ->
{noreply, State};
#state{state = X} = S ->
- ?LOGC(X =/= State#state.state, transition, X),
+ ?LOGC(X /= State#state.state, transition, X),
{noreply, S};
{stop, Reason} ->
?LOG(stop, Reason),
@@ -292,15 +292,12 @@ handle_info(T, #state{} = State) ->
?LOG(stop, T),
{stop, {shutdown, T}, State}
catch
- exit: {diameter_codec, encode, _} = Reason ->
+ exit: {diameter_codec, encode, T} = Reason ->
+ incr_error(send, T, State#state.dictionary),
?LOG(stop, Reason),
- %% diameter_codec:encode/2 emits an error report. Only
- %% indicate the probable reason here.
- diameter_lib:info_report(probable_configuration_error,
- insufficient_capabilities),
{stop, {shutdown, Reason}, State};
{?MODULE, Tag, Reason} ->
- ?LOG(Tag, {Reason, T}),
+ ?LOG(stop, Tag),
{stop, {shutdown, Reason}, State}
end.
%% The form of the throw caught here is historical. It's
@@ -476,12 +473,12 @@ send_CER(#state{state = {'Wait-Conn-Ack', Tmo},
orelse
close({already_connected, Remote, LCaps}),
CER = build_CER(S),
- ?LOG(send, 'CER'),
#diameter_packet{header = #diameter_header{end_to_end_id = Eid,
hop_by_hop_id = Hid}}
= Pkt
= encode(CER, Dict),
send(TPid, Pkt),
+ ?LOG(send, 'CER'),
start_timer(Tmo, S#state{state = {'Wait-CEA', Hid, Eid}}).
%% Register ourselves as connecting to the remote endpoint in
@@ -526,7 +523,6 @@ recv(#diameter_packet{header = #diameter_header{} = Hdr}
= S) ->
Name = diameter_codec:msg_name(Dict0, Hdr),
Pid ! {recv, self(), Name, Pkt},
- diameter_stats:incr({msg_id(Name, Hdr), recv}), %% count received
rcv(Name, Pkt, S);
recv(#diameter_packet{header = undefined,
@@ -553,42 +549,30 @@ recv(#diameter_header{length = Len}
recv(#diameter_header{}
= H,
#diameter_packet{bin = Bin},
- #state{length_errors = E}
- = S) ->
- invalid(E,
- invalid_message_length,
- recv,
- [size(Bin), bit_size(Bin) rem 8, H, S]);
+ #state{length_errors = E}) ->
+ T = {size(Bin), bit_size(Bin) rem 8, H},
+ invalid(E, message_length_mismatch, T);
-recv(false, Pkt, #state{length_errors = E} = S) ->
- invalid(E, truncated_header, recv, [Pkt, S]).
+recv(false, #diameter_packet{bin = Bin}, #state{length_errors = E}) ->
+ invalid(E, truncated_header, Bin).
%% Note that counters here only count discarded messages.
-invalid(E, Reason, F, A) ->
+invalid(E, Reason, T) ->
diameter_stats:incr(Reason),
- abort(E, Reason, F, A).
-
-abort(exit, Reason, F, A) ->
- diameter_lib:warning_report(Reason, {?MODULE, F, A}),
- throw({?MODULE, abort, Reason});
-
-abort(_, _, _, _) ->
+ E == exit andalso close({Reason, T}),
+ ?LOG(Reason, T),
ok.
-msg_id({_,_,_} = T, _) ->
- T;
-msg_id(_, Hdr) ->
- {_,_,_} = diameter_codec:msg_id(Hdr).
-
%% rcv/3
%% Incoming CEA.
-rcv('CEA',
+rcv('CEA' = N,
#diameter_packet{header = #diameter_header{end_to_end_id = Eid,
hop_by_hop_id = Hid}}
= Pkt,
#state{state = {'Wait-CEA', Hid, Eid}}
= S) ->
+ ?LOG(recv, N),
handle_CEA(Pkt, S);
%% Incoming CER
@@ -609,34 +593,71 @@ rcv('DPR' = N, Pkt, S) ->
%% DPA in response to DPR and with the expected identifiers.
rcv('DPA' = N,
#diameter_packet{header = #diameter_header{end_to_end_id = Eid,
- hop_by_hop_id = Hid}},
- #state{transport = TPid,
+ hop_by_hop_id = Hid}
+ = H}
+ = Pkt,
+ #state{dictionary = Dict0,
+ transport = TPid,
dpr = {Hid, Eid}}) ->
+ ?LOG(recv, N),
+ incr(recv, H, Dict0),
+ incr_rc(recv, diameter_codec:decode(Dict0, Pkt), Dict0),
diameter_peer:close(TPid),
{stop, N};
%% Ignore anything else, an unsolicited DPA in particular.
+rcv(N, #diameter_packet{header = H}, _)
+ when N == 'CER';
+ N == 'CEA';
+ N == 'DPR';
+ N == 'DPA' ->
+ ?LOG(ignored, N),
+ %% Note that these aren't counted in the normal recv counter.
+ diameter_stats:incr({diameter_codec:msg_id(H), recv, ignored}),
+ ok;
+
rcv(_, _, _) ->
ok.
+%% incr/3
+
+incr(Dir, Hdr, Dict0) ->
+ diameter_traffic:incr(Dir, Hdr, self(), Dict0).
+
+%% incr_rc/3
+
+incr_rc(Dir, Pkt, Dict0) ->
+ diameter_traffic:incr_rc(Dir, Pkt, self(), Dict0).
+
+%% incr_error/3
+
+incr_error(Dir, Pkt, Dict0) ->
+ diameter_traffic:incr_error(Dir, Pkt, self(), Dict0).
+
%% send/2
%% Msg here could be a #diameter_packet or a binary depending on who's
%% sending. In particular, the watchdog will send DWR as a binary
%% while messages coming from clients will be in a #diameter_packet.
send(Pid, Msg) ->
- diameter_stats:incr({diameter_codec:msg_id(Msg), send}),
diameter_peer:send(Pid, Msg).
%% handle_request/3
+%%
+%% Incoming CER or DPR.
-handle_request(Type, #diameter_packet{} = Pkt, #state{dictionary = D} = S) ->
- ?LOG(recv, Type),
- send_answer(Type, diameter_codec:decode(D, Pkt), S).
+handle_request(Name,
+ #diameter_packet{header = H} = Pkt,
+ #state{dictionary = Dict0} = S) ->
+ ?LOG(recv, Name),
+ incr(recv, H, Dict0),
+ send_answer(Name, diameter_codec:decode(Dict0, Pkt), S).
%% send_answer/3
send_answer(Type, ReqPkt, #state{transport = TPid, dictionary = Dict} = S) ->
+ incr_error(recv, ReqPkt, Dict),
+
#diameter_packet{header = H,
transport_data = TD}
= ReqPkt,
@@ -653,13 +674,19 @@ send_answer(Type, ReqPkt, #state{transport = TPid, dictionary = Dict} = S) ->
msg = Msg,
transport_data = TD},
- send(TPid, diameter_codec:encode(Dict, Pkt)),
+ AnsPkt = diameter_codec:encode(Dict, Pkt),
+
+ incr(send, AnsPkt, Dict),
+ incr_rc(send, AnsPkt, Dict),
+ send(TPid, AnsPkt),
+ ?LOG(send, ans(Type)),
eval(PostF, S).
+ans('CER') -> 'CEA';
+ans('DPR') -> 'DPA'.
+
eval([F|A], S) ->
apply(F, A ++ [S]);
-eval(ok, S) ->
- S;
eval(T, _) ->
close(T).
@@ -723,8 +750,8 @@ cea(CEA, RC, Dict0) ->
post('CER' = T, RC, Pkt, S) ->
{T, caps(S), {RC, Pkt}};
-post('DPR', _, _, _) ->
- ok.
+post('DPR' = T, _, _, #state{parent = Pid}) ->
+ [fun(S) -> Pid ! {T, self()}, S end].
rejected({capabilities_cb, _F, Reason}, T, S) ->
rejected(Reason, T, S);
@@ -734,7 +761,7 @@ rejected(discard, T, _) ->
rejected({N, Es}, T, S) ->
{answer('CER', N, failed_avp(N, Es), S), T};
rejected(N, T, S) ->
- rejected({N, []}, T, S).
+ {answer('CER', N, [], S), T}.
failed_avp(RC, [{RC, Avp} | _]) ->
[{'Failed-AVP', [[{'AVP', [Avp]}]]}];
@@ -848,28 +875,27 @@ recv_CER(CER, #state{service = Svc, dictionary = Dict}) ->
close({'CER', CER, Svc, Dict, Reason})
end.
-%% handle_CEA/1
+%% handle_CEA/2
-handle_CEA(#diameter_packet{bin = Bin}
+handle_CEA(#diameter_packet{header = H}
= Pkt,
#state{dictionary = Dict0,
service = #diameter_service{capabilities = LCaps}}
- = S)
- when is_binary(Bin) ->
- ?LOG(recv, 'CEA'),
+ = S) ->
+ incr(recv, H, Dict0),
- #diameter_packet{msg = CEA}
+ #diameter_packet{}
= DPkt
= diameter_codec:decode(Dict0, Pkt),
+ RC = result_code(incr_rc(recv, DPkt, Dict0)),
+
{SApps, IS, RCaps} = recv_CEA(DPkt, S),
#diameter_caps{origin_host = {OH, DH}}
= Caps
= capz(LCaps, RCaps),
- RC = Dict0:'#get-'('Result-Code', CEA),
-
%% Ensure that we don't already have a connection to the peer in
%% question. This isn't the peer election of 3588 except in the
%% sense that, since we don't know who we're talking to until we
@@ -877,7 +903,7 @@ handle_CEA(#diameter_packet{bin = Bin}
%% connection with the peer.
try
- ?IS_SUCCESS(RC)
+ is_integer(RC) andalso ?IS_SUCCESS(RC)
orelse ?THROW(RC),
[] == SApps
andalso ?THROW(no_common_application),
@@ -897,6 +923,11 @@ handle_CEA(#diameter_packet{bin = Bin}
%% capabilities exchange could send DIAMETER_LIMITED_SUCCESS = 2002,
%% even if this isn't required by RFC 3588.
+result_code({'Result-Code', N}) ->
+ N;
+result_code(_) ->
+ undefined.
+
%% recv_CEA/2
recv_CEA(#diameter_packet{header = #diameter_header{version
@@ -988,19 +1019,13 @@ capz(#diameter_caps{} = L, #diameter_caps{} = R) ->
tl(tuple_to_list(R)))]).
%% close/1
+%%
+%% A good function to trace on in case of problems with capabilities
+%% exchange.
close(Reason) ->
- report(Reason),
throw({?MODULE, close, Reason}).
-%% Could possibly log more here.
-report({M, _, _, _, _} = T)
- when M == 'CER';
- M == 'CEA' ->
- diameter_lib:error_report(failure, T);
-report(_) ->
- ok.
-
%% dpr/2
%%
%% The RFC isn't clear on whether DPR should be send in a non-Open
@@ -1034,7 +1059,7 @@ dpr(_Reason, _S) ->
%% process and contact it. (eg. diameter:service_info/2)
dpr([CB|Rest], [Reason | _] = Args, S) ->
- try diameter_lib:eval([CB | Args]) of
+ case diameter_lib:eval([CB | Args]) of
{dpr, Opts} when is_list(Opts) ->
send_dpr(Reason, Opts, S);
dpr ->
@@ -1044,14 +1069,7 @@ dpr([CB|Rest], [Reason | _] = Args, S) ->
ignore ->
dpr(Rest, Args, S);
T ->
- No = {disconnect_cb, T},
- diameter_lib:error_report(invalid, No),
- {stop, No}
- catch
- E:R ->
- No = {disconnect_cb, E, R, ?STACK},
- diameter_lib:error_report(failure, No),
- {stop, No}
+ ?ERROR({disconnect_cb, CB, Args, T})
end;
dpr([], [Reason | _], S) ->
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 1274e0fc48..b7cd311e02 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -499,9 +499,21 @@ transition(Req, S) ->
%% # terminate/2
%% ---------------------------------------------------------------------------
-terminate(Reason, #state{service_name = Name} = S) ->
+terminate(Reason, #state{service_name = Name, peerT = PeerT} = S) ->
send_event(Name, stop),
ets:delete(?STATE_TABLE, Name),
+
+ %% Communicate pending loss of any peers that connection_down/3
+ %% won't. This is needed when stopping a service since we don't
+ %% wait for watchdog state changes to take care of if. That this
+ %% takes place after deleting the state entry ensures that the
+ %% resulting failover by request processes accomplishes nothing.
+ ets:foldl(fun(#peer{pid = TPid}, _) ->
+ diameter_traffic:peer_down(TPid)
+ end,
+ ok,
+ PeerT),
+
shutdown == Reason %% application shutdown
andalso shutdown(application, S).
@@ -719,14 +731,27 @@ remotes(F) ->
L when is_list(L) ->
L;
T ->
- diameter_lib:error_report({invalid_return, T}, F),
+ ?LOG(invalid_return, {F,T}),
+ error_report(invalid_return, share_peers, F),
[]
catch
E:R ->
- diameter_lib:error_report({failure, {E, R, ?STACK}}, F),
+ ?LOG(failure, {E, R, F, diameter_lib:get_stacktrace()}),
+ error_report(failure, share_peers, F),
[]
end.
+%% error_report/3
+
+error_report(T, What, F) ->
+ Reason = io_lib:format("~s from ~p callback", [reason(T), What]),
+ diameter_lib:error_report(Reason, diameter_lib:eval_name(F)).
+
+reason(invalid_return) ->
+ "invalid return";
+reason(failure) ->
+ "failure".
+
%% ---------------------------------------------------------------------------
%% # start/3
%% ---------------------------------------------------------------------------
@@ -869,7 +894,7 @@ watchdog(TPid, [], ?WD_OKAY, ?WD_SUSPECT = To, Wd, State) ->
%% Watchdog has lost its connection.
watchdog(TPid, [], _, ?WD_DOWN = To, Wd, #state{peerT = PeerT} = S) ->
- close(Wd, S),
+ close(Wd),
watchdog_down(Wd, To, S),
ets:delete(PeerT, TPid);
@@ -1026,8 +1051,11 @@ peer_cb(App, F, A) ->
true
catch
E:R ->
- diameter_lib:error_report({failure, {E, R, ?STACK}},
- {App, F, A}),
+ %% Don't include arguments since a #diameter_caps{} strings
+ %% from the peer, which could be anything (especially, large).
+ [Mod|X] = App#diameter_app.module,
+ ?LOG(failure, {E, R, Mod, F, diameter_lib:get_stacktrace()}),
+ error_report(failure, F, {Mod, F, A ++ X}),
false
end.
@@ -1187,26 +1215,16 @@ tc(false = No, _, _) -> %% removed
%% another watchdog to be able to detect that it should transition
%% from initial into reopen rather than okay. That someone is either
%% the accepting watchdog upon reception of a CER from the previously
-%% connected peer, or us after connect_timer timeout.
+%% connected peer, or us after connect_timer timeout or immediately.
-close(#watchdog{type = connect}, _) ->
+close(#watchdog{type = connect}) ->
ok;
+
close(#watchdog{type = accept,
pid = Pid,
- ref = Ref,
- options = Opts},
- #state{service_name = SvcName}) ->
- c(Pid, diameter_config:have_transport(SvcName, Ref), Opts).
-
-%% Tell watchdog to (maybe) die later ...
-c(Pid, true, Opts) ->
+ options = Opts}) ->
Tc = connect_timer(Opts, 2*?DEFAULT_TC),
- erlang:send_after(Tc, Pid, close);
-
-%% ... or now.
-c(Pid, false, _Opts) ->
- Pid ! close.
-
+ erlang:send_after(Tc, Pid, close).
%% The RFC's only document the behaviour of Tc, our connect_timer,
%% for the establishment of connections but we also give
%% connect_timer semantics for a listener, being the time within
@@ -1260,13 +1278,14 @@ cm([#diameter_app{alias = Alias} = App], Req, From, Svc) ->
mod_state(Alias, ModS),
{T, RC};
T ->
- diameter_lib:error_report({invalid, T},
- {App, handle_call, Args}),
+ ModX = App#diameter_app.module,
+ ?LOG(invalid_return, {ModX, handle_call, Args, T}),
invalid
catch
E: Reason ->
- diameter_lib:error_report({failure, {E, Reason, ?STACK}},
- {App, handle_call, Args}),
+ ModX = App#diameter_app.module,
+ Stack = diameter_lib:get_stacktrace(),
+ ?LOG(failure, {E, Reason, ModX, handle_call, Stack}),
failure
end;
@@ -1389,6 +1408,8 @@ pick_peer(Local, Remote, Pid, _SvcName, #diameter_app{mutable = true} = App)
case call_service(Pid, {pick_peer, Local, Remote, App}) of
{TPid, _} = T when is_pid(TPid) ->
T;
+ false = No ->
+ No;
{error, _} ->
false
end;
@@ -1422,13 +1443,16 @@ pick_peer(Local,
T; %% Accept returned state in the immutable
{false = No, S} -> %% case as long it isn't changed.
No;
- T ->
- diameter_lib:error_report({invalid, T, App},
- {App, pick_peer, Args})
+ T when M ->
+ ModX = App#diameter_app.module,
+ ?LOG(invalid_return, {ModX, pick_peer, T}),
+ false
catch
- E: Reason ->
- diameter_lib:error_report({failure, {E, Reason, ?STACK}},
- {App, pick_peer, Args})
+ E: Reason when M ->
+ ModX = App#diameter_app.module,
+ Stack = diameter_lib:get_stacktrace(),
+ ?LOG(failure, {E, Reason, ModX, pick_peer, Stack}),
+ false
end.
%% peers/4
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index 7fbb306b02..5fac61f416 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -31,6 +31,11 @@
%% towards diameter_watchdog
-export([receive_message/4]).
+%% towards diameter_peer_fsm and diameter_watchdog
+-export([incr/4,
+ incr_error/4,
+ incr_rc/4]).
+
%% towards diameter_service
-export([make_recvdata/1,
peer_up/1,
@@ -44,6 +49,8 @@
-include_lib("diameter/include/diameter.hrl").
-include("diameter_internal.hrl").
+-define(LOGX(Reason, T), begin ?LOG(Reason, T), x({Reason, T}) end).
+
-define(RELAY, ?DIAMETER_DICT_RELAY).
-define(BASE, ?DIAMETER_DICT_COMMON). %% Note: the RFC 3588 dictionary
@@ -109,6 +116,67 @@ peer_down(TPid) ->
failover(TPid).
%% ---------------------------------------------------------------------------
+%% incr/4
+%% ---------------------------------------------------------------------------
+
+incr(Dir, #diameter_packet{header = H}, TPid, Dict) ->
+ incr(Dir, H, TPid, Dict);
+
+incr(Dir, #diameter_header{} = H, TPid, Dict) ->
+ incr(TPid, {msg_id(H, Dict), Dir}).
+
+%% ---------------------------------------------------------------------------
+%% incr_error/4
+%% ---------------------------------------------------------------------------
+
+%% Decoded message without errors.
+incr_error(recv, #diameter_packet{errors = []}, _, _) ->
+ ok;
+
+incr_error(recv = D, #diameter_packet{header = H}, TPid, Dict) ->
+ incr_error(D, H, TPid, Dict);
+
+%% Encoded message with errors and an identifiable header ...
+incr_error(send = D, {_, _, #diameter_header{} = H}, TPid, Dict) ->
+ incr_error(D, H, TPid, Dict);
+
+%% ... or not.
+incr_error(send = D, {_,_}, TPid, _) ->
+ incr_error(D, unknown, TPid);
+
+incr_error(Dir, #diameter_header{} = H, TPid, Dict) ->
+ incr_error(Dir, msg_id(H, Dict), TPid);
+
+incr_error(Dir, Id, TPid, _) ->
+ incr_error(Dir, Id, TPid).
+
+incr_error(Dir, Id, TPid) ->
+ incr(TPid, {Id, Dir, error}).
+
+%% ---------------------------------------------------------------------------
+%% incr_rc/4
+%% ---------------------------------------------------------------------------
+
+-spec incr_rc(send|recv, Pkt, TPid, Dict0)
+ -> {Counter, non_neg_integer()}
+ | Reason
+ when Pkt :: #diameter_packet{},
+ TPid :: pid(),
+ Dict0 :: module(),
+ Counter :: {'Result-Code', integer()}
+ | {'Experimental-Result', integer(), integer()},
+ Reason :: atom().
+
+incr_rc(Dir, Pkt, TPid, Dict0) ->
+ try
+ incr_rc(Dir, Pkt, Dict0, TPid, Dict0)
+ catch
+ exit: {E,_} when E == no_result_code;
+ E == invalid_error_bit ->
+ E
+ end.
+
+%% ---------------------------------------------------------------------------
%% pending/1
%% ---------------------------------------------------------------------------
@@ -182,7 +250,7 @@ spawn_request(TPid, Pkt, Dict0, Opts, RecvData) ->
spawn_opt(fun() -> recv_request(TPid, Pkt, Dict0, RecvData) end, Opts)
catch
error: system_limit = E -> %% discard
- ?LOG({error, E}, now())
+ ?LOG(error, E)
end.
%% ---------------------------------------------------------------------------
@@ -211,7 +279,9 @@ recv_R({#diameter_app{id = Id, dictionary = Dict} = App, Caps},
Pkt0,
Dict0,
RecvData) ->
+ incr(recv, Pkt0, TPid, Dict),
Pkt = errors(Id, diameter_codec:decode(Id, Dict, Pkt0)),
+ incr_error(recv, Pkt, TPid, Dict),
{Caps, Pkt, App, recv_R(App, TPid, Dict0, Caps, RecvData, Pkt)};
%% Note that the decode is different depending on whether or not Id is
%% ?APP_ID_RELAY.
@@ -283,23 +353,25 @@ rc(N) ->
%% This error is returned when a request is received with an invalid
%% message length.
-errors(_, #diameter_packet{header = #diameter_header{length = Len},
+errors(_, #diameter_packet{header = #diameter_header{length = Len} = H,
bin = Bin,
errors = Es}
= Pkt)
when Len < 20;
0 /= Len rem 4;
8*Len /= bit_size(Bin) ->
+ ?LOG(invalid_message_length, {H, bit_size(Bin)}),
Pkt#diameter_packet{errors = [5015 | Es]};
%% DIAMETER_UNSUPPORTED_VERSION 5011
%% This error is returned when a request was received, whose version
%% number is unsupported.
-errors(_, #diameter_packet{header = #diameter_header{version = V},
+errors(_, #diameter_packet{header = #diameter_header{version = V} = H,
errors = Es}
= Pkt)
when V /= ?DIAMETER_VERSION ->
+ ?LOG(unsupported_version, H),
Pkt#diameter_packet{errors = [5011 | Es]};
%% DIAMETER_COMMAND_UNSUPPORTED 3001
@@ -307,12 +379,13 @@ errors(_, #diameter_packet{header = #diameter_header{version = V},
%% recognize or support. This MUST be used when a Diameter node
%% receives an experimental command that it does not understand.
-errors(Id, #diameter_packet{header = #diameter_header{is_proxiable = P},
+errors(Id, #diameter_packet{header = #diameter_header{is_proxiable = P} = H,
msg = M,
errors = Es}
= Pkt)
when ?APP_ID_RELAY /= Id, undefined == M; %% don't know the command
?APP_ID_RELAY == Id, not P -> %% command isn't proxiable
+ ?LOG(command_unsupported, H),
Pkt#diameter_packet{errors = [3001 | Es]};
%% DIAMETER_INVALID_HDR_BITS 3008
@@ -321,9 +394,11 @@ errors(Id, #diameter_packet{header = #diameter_header{is_proxiable = P},
%% inconsistent with the command code's definition.
errors(_, #diameter_packet{header = #diameter_header{is_request = true,
- is_error = true},
+ is_error = true}
+ = H,
errors = Es}
= Pkt) ->
+ ?LOG(invalid_hdr_bits, H),
Pkt#diameter_packet{errors = [3008 | Es]};
%% Green.
@@ -479,7 +554,6 @@ answer_message(RC,
origin_realm = {OR,_}},
Dict0,
Pkt) ->
- ?LOG({error, RC}, Pkt),
{Dict0, answer_message(OH, OR, RC, Dict0, Pkt)}.
%% resend/7
@@ -595,9 +669,11 @@ reply([Msg], Dict, TPid, Dict0, Fs, ReqPkt)
reply(Msg, Dict, TPid, Dict0, Fs, ReqPkt) ->
Pkt = encode(Dict,
+ TPid,
reset(make_answer_packet(Msg, ReqPkt), Dict, Dict0),
Fs),
- incr(send, Pkt, Dict, TPid, Dict0), %% count outgoing result codes
+ incr(send, Pkt, TPid, Dict),
+ incr_rc(send, Pkt, Dict, TPid, Dict0), %% count outgoing
send(TPid, Pkt).
%% reset/3
@@ -962,35 +1038,48 @@ find(Pred, [H|T]) ->
%% code, the missing vendor id, and a zero filled payload of the minimum
%% required length for the omitted AVP will be added.
-%% incr/4
+%% incr_rc/5
%%
%% Increment a stats counter for result codes in incoming and outgoing
%% answers.
%% Outgoing message as binary: don't count. (Sending binaries is only
%% partially supported.)
-incr(_, #diameter_packet{msg = undefined}, _, _, _) ->
- ok;
-
-%% Incoming with decode errors.
-incr(recv = D, #diameter_packet{header = H, errors = [_|_]}, _, TPid, _) ->
- incr(TPid, {diameter_codec:msg_id(H), D, error});
+incr_rc(_, #diameter_packet{msg = undefined = No}, _, _, _) ->
+ No;
-%% Incoming without errors or outgoing. Outgoing with encode errors
-%% never gets here since encode fails.
-incr(Dir, Pkt, Dict, TPid, Dict0) ->
+%% Incoming or outgoing. Outgoing with encode errors never gets here
+%% since encode fails.
+incr_rc(Dir, Pkt, Dict, TPid, Dict0) ->
#diameter_packet{header = #diameter_header{is_error = E}
= Hdr,
- msg = Rec}
+ msg = Msg,
+ errors = Es}
= Pkt,
- RC = int(get_avp_value(Dict, 'Result-Code', Rec)),
+ Id = msg_id(Hdr, Dict),
+
+ %% Count incoming decode errors.
+ recv /= Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, Dict),
- %% Exit on an improper Result-Code.
+ %% Exit on a missing result code.
+ T = rc_counter(Dict, Msg),
+ T == false andalso ?LOGX(no_result_code, {Dict, Dir, Hdr}),
+ {Ctr, RC} = T,
+
+ %% Or on an inappropriate value.
is_result(RC, E, Dict0)
- orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]),
+ orelse ?LOGX(invalid_error_bit, {Dict, Dir, Hdr, RC}),
+
+ incr(TPid, {Id, Dir, Ctr}),
+ Ctr.
- irc(TPid, Hdr, Dir, rc_counter(Dict, Rec, RC)).
+%% Only count on known keeps so as not to be vulnerable to attack:
+%% there are 2^32 (application ids) * 2^24 (command codes) * 2 (R-bits)
+%% = 2^57 Ids for an attacker to choose from.
+msg_id(Hdr, Dict) ->
+ {_ApplId, Code, R} = Id = diameter_codec:msg_id(Hdr),
+ choose('' == Dict:msg_name(Code, 0 == R), unknown, Id).
%% No E-bit: can't be 3xxx.
is_result(RC, false, _Dict0) ->
@@ -1006,12 +1095,6 @@ is_result(RC, true, _) ->
orelse
5000 =< RC andalso RC < 6000.
-irc(_, _, _, undefined) ->
- false;
-
-irc(TPid, Hdr, Dir, Ctr) ->
- incr(TPid, {diameter_codec:msg_id(Hdr), Dir, Ctr}).
-
%% incr/2
incr(TPid, Counter) ->
@@ -1024,14 +1107,16 @@ incr(TPid, Counter) ->
%% All Diameter answer messages defined in vendor-specific
%% applications MUST include either one Result-Code AVP or one
%% Experimental-Result AVP.
-%%
-%% Maintain statistics assuming one or the other, not both, which is
-%% surely the intent of the RFC.
-rc_counter(Dict, Rec, undefined) ->
- rcc(get_avp_value(Dict, 'Experimental-Result', Rec));
-rc_counter(_, _, RC) ->
- {'Result-Code', RC}.
+rc_counter(Dict, Msg) ->
+ rcc(Dict, Msg, int(get_avp_value(Dict, 'Result-Code', Msg))).
+
+rcc(Dict, Msg, undefined) ->
+ rcc(get_avp_value(Dict, 'Experimental-Result', Msg));
+
+rcc(_, _, N)
+ when is_integer(N) ->
+ {{'Result-Code', N}, N}.
%% Outgoing answers may be in any of the forms messages can be sent
%% in. Incoming messages will be records. We're assuming here that the
@@ -1039,12 +1124,12 @@ rc_counter(_, _, RC) ->
rcc([{_,_,N} = T | _])
when is_integer(N) ->
- T;
+ {T,N};
rcc({_,_,N} = T)
when is_integer(N) ->
- T;
+ {T,N};
rcc(_) ->
- undefined.
+ false.
%% Extract the first good looking integer. There's no guarantee
%% that what we're looking for has arity 1.
@@ -1057,13 +1142,6 @@ int(N)
int(_) ->
undefined.
--spec x(any(), atom(), list()) -> no_return().
-
-%% Warn and exit request process on errors in an incoming answer.
-x(Reason, F, A) ->
- diameter_lib:warning_report(Reason, {?MODULE, F, A}),
- x(Reason).
-
x(T) ->
exit(T).
@@ -1305,7 +1383,7 @@ send_R(Pkt0,
{Pid, Ref},
SvcName,
Fs) ->
- Pkt = encode(Dict, Pkt0, Fs),
+ Pkt = encode(Dict, TPid, Pkt0, Fs),
#options{timeout = Timeout}
= Opts,
@@ -1370,11 +1448,19 @@ handle_answer(SvcName,
%% want to examine the answer?
handle_A(Pkt, SvcName, Dict, Dict0, App, #request{transport = TPid} = Req) ->
+ incr(recv, Pkt, TPid, Dict),
+
try
- incr(recv, Pkt, Dict, TPid, Dict0) %% count incoming result codes
+ incr_rc(recv, Pkt, Dict, TPid, Dict0) %% count incoming
of
_ -> answer(Pkt, SvcName, App, Req)
catch
+ exit: {no_result_code, _} ->
+ %% RFC 6733 requires one of Result-Code or
+ %% Experimental-Result, but the decode will have detected
+ %% a missing AVP. If both are optional in the dictionary
+ %% then this isn't a decode error: just continue on.
+ answer(Pkt, SvcName, App, Req);
exit: {invalid_error_bit, RC} ->
#diameter_packet{errors = Es}
= Pkt,
@@ -1401,11 +1487,16 @@ a(#diameter_packet{errors = Es}
callback == AE ->
cb(ModX, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}]);
-a(Pkt, SvcName, _, report, Req) ->
- x(errors, handle_answer, [SvcName, Req, Pkt]);
+a(Pkt, SvcName, _, AE, _) ->
+ a(Pkt#diameter_packet.header, SvcName, AE).
+
+a(Hdr, SvcName, report) ->
+ MFA = {?MODULE, handle_answer, [SvcName, Hdr]},
+ diameter_lib:warning_report(errors, MFA),
+ a(Hdr, SvcName, discard);
-a(Pkt, SvcName, _, discard, Req) ->
- x({errors, handle_answer, [SvcName, Req, Pkt]}).
+a(Hdr, SvcName, discard) ->
+ x({answer_errors, {SvcName, Hdr}}).
%% Note that we don't check that the application id in the answer's
%% header is what we expect. (TODO: Does the rfc says anything about
@@ -1463,10 +1554,10 @@ msg(#diameter_packet{msg = undefined, bin = Bin}) ->
msg(#diameter_packet{msg = Msg}) ->
Msg.
-%% encode/3
+%% encode/4
-encode(Dict, Pkt, Fs) ->
- P = encode(Dict, Pkt),
+encode(Dict, TPid, Pkt, Fs) ->
+ P = encode(Dict, TPid, Pkt),
eval_packet(P, Fs),
P.
@@ -1478,11 +1569,17 @@ encode(Dict, Pkt, Fs) ->
%% support retransmission but is useful for test.
%% A message to be encoded.
-encode(Dict, #diameter_packet{bin = undefined} = Pkt) ->
- diameter_codec:encode(Dict, Pkt);
+encode(Dict, TPid, #diameter_packet{bin = undefined} = Pkt) ->
+ try
+ diameter_codec:encode(Dict, Pkt)
+ catch
+ exit: {diameter_codec, encode, T} = Reason ->
+ incr_error(send, T, TPid, Dict),
+ exit(Reason)
+ end;
%% An encoded binary: just send.
-encode(_, #diameter_packet{} = Pkt) ->
+encode(_, _, #diameter_packet{} = Pkt) ->
Pkt.
%% send_request/5
@@ -1579,13 +1676,13 @@ resend_request(Pkt0,
SvcName,
Tmo,
Fs) ->
- Pkt = encode(Dict, Pkt0, Fs),
+ Pkt = encode(Dict, TPid, Pkt0, Fs),
Req = Req0#request{transport = TPid,
packet = Pkt0,
caps = Caps},
- ?LOG(retransmission, Req),
+ ?LOG(retransmission, Pkt#diameter_packet.header),
TRef = send_request(TPid, Pkt, Req, SvcName, Tmo),
{TRef, Req}.
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index 53e659e3f6..eff5096745 100644
--- a/lib/diameter/src/base/diameter_watchdog.erl
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -49,8 +49,6 @@
-define(IS_NATURAL(N), (is_integer(N) andalso 0 =< N)).
--define(CHOOSE(B,T,F), if (B) -> T; true -> F end).
-
-record(config,
{suspect = 1 :: non_neg_integer(), %% OKAY -> SUSPECT
okay = 3 :: non_neg_integer()}). %% REOPEN -> OKAY
@@ -221,7 +219,6 @@ dict0(_, _, Acc) ->
Acc.
config_error(T) ->
- diameter_lib:error_report(configuration_error, T),
exit({shutdown, {configuration_error, T}}).
%% handle_call/3
@@ -270,7 +267,7 @@ event(Msg,
TPid = tpid(F,T),
E = {[TPid | data(Msg, TPid, From, To)], From, To},
send(Pid, {watchdog, self(), E}),
- ?LOG(transition, {self(), E}).
+ ?LOG(transition, {From, To}).
data(Msg, TPid, reopen, okay) ->
{recv, TPid, 'DWA', _Pkt} = Msg, %% assert
@@ -313,14 +310,13 @@ code_change(_, State, _) ->
%% The state transitions documented here are extracted from RFC 3539,
%% the commentary is ours.
-%% Service or watchdog is telling the watchdog of an accepting
-%% transport to die after connect_timer expiry or reestablished
-%% connection (in another transport process) respectively.
-transition(close, #watchdog{status = down}) ->
+%% Service is telling the watchdog of an accepting transport to die
+%% following transport death in state INITIAL, or after connect_timer
+%% expiry; or another watchdog is saying the same after reestablishing
+%% a connection previously had by this one.
+transition(close, #watchdog{}) ->
{{accept, _}, _, _} = getr(restart), %% assert
stop;
-transition(close, #watchdog{}) ->
- ok;
%% Service is asking for the peer to be taken down gracefully.
transition({shutdown, Pid, _}, #watchdog{parent = Pid,
@@ -332,6 +328,11 @@ transition({shutdown = T, Pid, Reason}, #watchdog{parent = Pid,
send(TPid, {T, self(), Reason}),
S#watchdog{shutdown = true};
+%% Transport is telling us that DPA has been sent in response to DPR:
+%% its death should lead to ours.
+transition({'DPR', TPid}, #watchdog{transport = TPid} = S) ->
+ S#watchdog{shutdown = true};
+
%% Parent process has died,
transition({'DOWN', _, process, Pid, _Reason},
#watchdog{parent = Pid}) ->
@@ -403,18 +404,39 @@ transition({open = Key, TPid, _Hosts, T},
%% REOPEN Connection down CloseConnection()
%% SetWatchdog() DOWN
+%% Transport has died after DPA or service requested termination ...
transition({'DOWN', _, process, TPid, _Reason},
#watchdog{transport = TPid,
shutdown = true}) ->
stop;
+%% ... or not.
transition({'DOWN', _, process, TPid, _Reason},
#watchdog{transport = TPid,
- status = T}
- = S) ->
- set_watchdog(S#watchdog{status = ?CHOOSE(initial == T, T, down),
- pending = false,
- transport = undefined});
+ status = T,
+ restrict = {_,R}}
+ = S0) ->
+ S = S0#watchdog{pending = false,
+ transport = undefined},
+ {{M,_}, _, _} = getr(restart),
+
+ %% Close an accepting watchdog immediately if there's no
+ %% restriction on the number of connections to the same peer: the
+ %% state machine never enters state REOPEN in this case. The
+ %% 'close' message (instead of stop) is so as not to bypass the
+ %% sending of messages to the service process in handle_info/2.
+
+ if T /= initial, M == accept, not R ->
+ send(self(), close),
+ S#watchdog{status = down};
+ T /= initial ->
+ set_watchdog(S#watchdog{status = down});
+ M == connect ->
+ set_watchdog(S);
+ M == accept ->
+ send(self(), close),
+ S
+ end;
%% Incoming message.
transition({recv, TPid, Name, Pkt}, #watchdog{transport = TPid} = S) ->
@@ -454,9 +476,7 @@ encode(dwr = M, Dict0, Mask) ->
hop_by_hop_id = Seq},
Pkt = #diameter_packet{header = Hdr,
msg = Msg},
- #diameter_packet{bin = Bin} = diameter_codec:encode(Dict0, Pkt),
- Bin;
-
+ diameter_codec:encode(Dict0, Pkt);
encode(dwa, Dict0, #diameter_packet{header = H, transport_data = TD}
= ReqPkt) ->
@@ -525,10 +545,14 @@ send_watchdog(#watchdog{pending = false,
dictionary = Dict0,
sequence = Mask}
= S) ->
- send(TPid, {send, encode(dwr, Dict0, Mask)}),
+ #diameter_packet{bin = Bin} = EPkt = encode(dwr, Dict0, Mask),
+ diameter_traffic:incr(send, EPkt, TPid, Dict0),
+ send(TPid, {send, Bin}),
?LOG(send, 'DWR'),
S#watchdog{pending = true}.
+%% Dont' count encode errors since we don't expect any on DWR/DWA.
+
%% recv/3
recv(Name, Pkt, S) ->
@@ -545,13 +569,29 @@ recv(Name, Pkt, S) ->
rcv('DWR', Pkt, #watchdog{transport = TPid,
dictionary = Dict0}) ->
- send(TPid, {send, encode(dwa, Dict0, Pkt)}),
+ ?LOG(recv, 'DWR'),
+ DPkt = diameter_codec:decode(Dict0, Pkt),
+ diameter_traffic:incr(recv, DPkt, TPid, Dict0),
+ diameter_traffic:incr_error(recv, DPkt, TPid, Dict0),
+ EPkt = encode(dwa, Dict0, Pkt),
+ diameter_traffic:incr(send, EPkt, TPid, Dict0),
+ diameter_traffic:incr_rc(send, EPkt, TPid, Dict0),
+
+ send(TPid, {send, EPkt}),
?LOG(send, 'DWA');
+rcv('DWA', Pkt, #watchdog{transport = TPid,
+ dictionary = Dict0}) ->
+ ?LOG(recv, 'DWA'),
+ diameter_traffic:incr(recv, Pkt, TPid, Dict0),
+ diameter_traffic:incr_rc(recv,
+ diameter_codec:decode(Dict0, Pkt),
+ TPid,
+ Dict0);
+
rcv(N, _, _)
when N == 'CER';
N == 'CEA';
- N == 'DWA';
N == 'DPR';
N == 'DPA' ->
false;
@@ -740,7 +780,7 @@ timeout(#watchdog{status = T} = S)
restart(#watchdog{transport = undefined} = S) ->
restart(getr(restart), S);
-restart(S) ->
+restart(S) -> %% reconnect has won race with timeout
S.
%% restart/2
@@ -770,9 +810,10 @@ restart({{connect, _} = T, Opts, Svc},
%% die. Note that a state machine never enters state REOPEN in this
%% case.
restart({{accept, _}, _, _}, #watchdog{restrict = {_, false}}) ->
- stop;
+ stop; %% 'DOWN' was in old code: 'close' was not sent
-%% Otherwise hang around until told to die.
+%% Otherwise hang around until told to die, either by the service or
+%% by another watchdog.
restart({{accept, _}, _, _}, S) ->
S.
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl
index 22422f2ef2..5a068c1a25 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-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,7 +31,8 @@
%% on the beam file of another dictionary.
%%
--export([from_dict/4]).
+-export([from_dict/4,
+ is_printable_ascii/1]). %% used by ?TERM/1 in diameter_forms.hrl
-include("diameter_forms.hrl").
-include("diameter_vsn.hrl").
@@ -121,6 +122,9 @@ eraser(Key) ->
%% ===========================================================================
%% ===========================================================================
+is_printable_ascii(C) ->
+ 16#20 =< C andalso C =< 16#7F.
+
get_value(Key, Plist) ->
proplists:get_value(Key, Plist, []).
diff --git a/lib/diameter/src/compiler/diameter_dict_util.erl b/lib/diameter/src/compiler/diameter_dict_util.erl
index 136bba16cb..cf4741e563 100644
--- a/lib/diameter/src/compiler/diameter_dict_util.erl
+++ b/lib/diameter/src/compiler/diameter_dict_util.erl
@@ -731,8 +731,8 @@ no_messages_without_id(Dict) ->
%% explode/4
%%
-%% {avp_vendor_id, AvpName} -> [Lineno, Id::integer()]
-%% {custom_types|codecs|inherits, AvpName} -> [Lineno, Mod::string()]
+%% {avp_vendor_id, AvpName} -> [Lineno, Id::integer()]
+%% {custom|inherits, AvpName} -> [Lineno, Mod::string()]
explode({_, Line, AvpName}, Dict, {_, _, X} = T, K) ->
true = K /= avp_vendor_id orelse is_uint32(T, [K]),
@@ -1094,7 +1094,7 @@ explode_avps([{_, Line, Name} | Toks], Dict) ->
Vid = avp_vendor_id(Flags, Name, Line, Dict),
%% An AVP is uniquely defined by its AVP code and vendor id (if any).
- %% Ensure there are no duplicate.
+ %% Ensure there are no duplicates.
store_new({avp_types, {Code, Vid}},
[Line, Name],
Dict,
@@ -1302,8 +1302,7 @@ x({K, {Name, AvpName}}, [Line | _], Dict)
%% Ditto.
x({K, AvpName}, [Line | _], Dict)
when K == avp_vendor_id;
- K == custom_types;
- K == codecs ->
+ K == custom ->
true = avp_is_defined(AvpName, Dict, Line);
%% Ensure that all local AVP's of type Grouped are also present in @grouped.
diff --git a/lib/diameter/src/compiler/diameter_forms.hrl b/lib/diameter/src/compiler/diameter_forms.hrl
index 9b14c1715a..dd03401b9e 100644
--- a/lib/diameter/src/compiler/diameter_forms.hrl
+++ b/lib/diameter/src/compiler/diameter_forms.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -57,4 +57,6 @@
-define(FIELDS(Fs), [{?record_field, ?ATOM(F), V} || {F,V} <- Fs]).
%% Literal term.
--define(TERM(T), erl_parse:abstract(T, ?LINE)).
+-define(TERM(T), erl_parse:abstract(T, [
+ {line, ?LINE},
+ {encoding, fun diameter_codegen:is_printable_ascii/1}])).
diff --git a/lib/diameter/src/compiler/diameter_make.erl b/lib/diameter/src/compiler/diameter_make.erl
index adc7808e49..72f5d36da4 100644
--- a/lib/diameter/src/compiler/diameter_make.erl
+++ b/lib/diameter/src/compiler/diameter_make.erl
@@ -232,21 +232,29 @@ identify([Vsn | [T|_] = ParseD])
identify({path, File} = T) ->
{T, File};
identify(File) ->
- Bin = iolist_to_binary([File]),
- case is_path(Bin) of
+ case is_path([File]) of
true -> {{path, File}, File};
- false -> {Bin, ?DEFAULT_DICT_FILE}
+ false -> {File, ?DEFAULT_DICT_FILE}
end.
-%% Interpret anything containing \n or \r as a literal dictionary,
-%% otherwise a path. (Which might be the wrong guess in the worst case.)
-is_path(Bin) ->
- try
- [throw(C) || <<C>> <= Bin, $\n == C orelse $\r == C],
- true
- catch
- throw:_ -> false
- end.
+%% Interpret anything containing \n or \r as a literal dictionary.
+
+is_path([<<C,B/binary>> | T]) ->
+ is_path([C, B | T]);
+
+is_path([[C|L] | T]) ->
+ is_path([C, L | T]);
+
+is_path([C|_])
+ when $\n == C;
+ $\r == C ->
+ false;
+
+is_path([_|T]) ->
+ is_path(T);
+
+is_path([]) ->
+ true.
make(File, Opts, Dict) ->
ok(lists:foldl(fun(M,A) -> [make(File, Opts, Dict, M) | A] end,
diff --git a/lib/diameter/src/diameter.app.src b/lib/diameter/src/diameter.app.src
index ceefb9b398..ac1d847753 100644
--- a/lib/diameter/src/diameter.app.src
+++ b/lib/diameter/src/diameter.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,9 +20,27 @@
{application, diameter,
[{description, "Diameter protocol"},
{vsn, "%VSN%"},
- {modules, [%MODULES%]},
+ {modules, [
+ %MODULES%
+ %,%COMPILER%
+ %,%INFO%
+ ]},
{registered, [%REGISTERED%]},
- {applications, [stdlib, kernel]},
+ {applications, [
+ {stdlib, "2.0"}, {kernel, "3.0"}%, {erts, "6.0"}
+ %% {syntax-tools, "1.6.14"}
+ %% {runtime-tools, "1.8.14"}
+ %, {ssl, "5.3.4"}
+ ]},
{env, []},
- {mod, {diameter_app, []}}
+ {mod, {diameter_app, []}},
+ {runtime_dependencies, [
+ ]}
+ %%
+ %% Note that ssl is only required if configured on TCP transports,
+ %% and syntax-tools and runtime-tools are only required if the
+ %% dictionary compiler and debug modules (respectively) are
+ %% needed/wanted at runtime, which they typically aren't. These
+ %% modules are the two commented lines in the 'modules' tuple.
+ %%
]}.
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 0d421c229e..b7b9662383 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -34,7 +34,19 @@
{"1.4.2", [{restart_application, diameter}]}, %% R16B01
{"1.4.3", [{restart_application, diameter}]}, %% R16B02
{"1.4.4", [{restart_application, diameter}]},
- {"1.5", [{restart_application, diameter}]} %% R16B03
+ {"1.5", [{restart_application, diameter}]}, %% R16B03
+ {"1.6", [{load_module, diameter_lib}, %% 17.0
+ {load_module, diameter_traffic},
+ {load_module, diameter_watchdog},
+ {load_module, diameter_peer_fsm},
+ {load_module, diameter_service},
+ {load_module, diameter_gen_base_rfc6733},
+ {load_module, diameter_gen_acct_rfc6733},
+ {load_module, diameter_gen_base_rfc3588},
+ {load_module, diameter_gen_accounting},
+ {load_module, diameter_gen_relay},
+ {load_module, diameter_codec},
+ {load_module, diameter_sctp}]}
],
[
{"0.9", [{restart_application, diameter}]},
@@ -51,6 +63,18 @@
{"1.4.2", [{restart_application, diameter}]},
{"1.4.3", [{restart_application, diameter}]},
{"1.4.4", [{restart_application, diameter}]},
- {"1.5", [{restart_application, diameter}]}
+ {"1.5", [{restart_application, diameter}]},
+ {"1.6", [{load_module, diameter_sctp},
+ {load_module, diameter_codec},
+ {load_module, diameter_gen_relay},
+ {load_module, diameter_gen_accounting},
+ {load_module, diameter_gen_base_rfc3588},
+ {load_module, diameter_gen_acct_rfc6733},
+ {load_module, diameter_gen_base_rfc6733},
+ {load_module, diameter_service},
+ {load_module, diameter_peer_fsm},
+ {load_module, diameter_watchdog},
+ {load_module, diameter_traffic},
+ {load_module, diameter_lib}]}
]
}.
diff --git a/lib/diameter/src/base/diameter_dbg.erl b/lib/diameter/src/info/diameter_dbg.erl
index 5b0ac3a3b6..b5b3983afa 100644
--- a/lib/diameter/src/base/diameter_dbg.erl
+++ b/lib/diameter/src/info/diameter_dbg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,20 +17,22 @@
%% %CopyrightEnd%
%%
+%%
+%% Information and debug functions.
+%%
+
-module(diameter_dbg).
-export([table/1,
tables/0,
fields/1,
- help/0,
modules/0,
versions/0,
version_info/0,
compiled/0,
procs/0,
latest/0,
- nl/0,
- log/4]).
+ nl/0]).
-export([diameter_config/0,
diameter_peer/0,
@@ -52,11 +54,9 @@
tp/1]).
-include_lib("diameter/include/diameter.hrl").
--include("diameter_internal.hrl").
-
--define(INFO, diameter_info).
--define(SEP(), ?INFO:sep()).
+-define(APP, diameter).
+-define(I, diameter_info).
-define(LOCAL, [diameter_config,
diameter_peer,
@@ -68,27 +68,16 @@
-define(VALUES(Rec), tl(tuple_to_list(Rec))).
-log(_Slogan, _Mod, _Line, _Details) ->
- ok.
-
-%%% ----------------------------------------------------------
-%%% # help()
-%%% ----------------------------------------------------------
-
-help() ->
- not_yet_implemented.
-
-%%% ----------------------------------------------------------
-%%% # table(TableName)
-%%%
-%%% Input: TableName = diameter table containing record entries.
-%%%
-%%% Output: Count | undefined
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # table(TableName)
+%%
+%% Pretty-print a diameter table. Returns the number of records
+%% printed, or undefined.
+%% ----------------------------------------------------------
table(T)
when (T == diameter_peer) orelse (T == diameter_reg) ->
- ?INFO:format(collect(T), fields(T), fun ?INFO:split/2);
+ ?I:format(collect(T), fields(T), fun ?I:split/2);
table(Table)
when is_atom(Table) ->
@@ -96,7 +85,7 @@ table(Table)
undefined = No ->
No;
Fields ->
- ?INFO:format(Table, Fields, fun split/2)
+ ?I:format(Table, Fields, fun split/2)
end.
split([started, name | Fs], [S, N | Vs]) ->
@@ -107,9 +96,9 @@ split([[F|FT]|Fs], [Rec|Vs]) ->
split([F|Fs], [V|Vs]) ->
{F, Fs, V, Vs}.
-%%% ----------------------------------------------------------
-%%% # TableName()
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # TableName()
+%% ----------------------------------------------------------
-define(TABLE(Name), Name() -> table(Name)).
@@ -121,16 +110,15 @@ split([F|Fs], [V|Vs]) ->
?TABLE(diameter_service).
?TABLE(diameter_stats).
-%%% ----------------------------------------------------------
-%%% # tables()
-%%%
-%%% Output: Number of records output.
-%%%
-%%% Description: Pretty-print records in diameter tables from all nodes.
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # tables()
+%%
+%% Pretty-print diameter tables from all nodes. Returns the number of
+%% records printed.
+%% ----------------------------------------------------------
tables() ->
- ?INFO:format(field(?LOCAL), fun split/3, fun collect/1).
+ ?I:format(field(?LOCAL), fun split/3, fun collect/1).
field(Tables) ->
lists:map(fun(T) -> {T, fields(T)} end, lists:sort(Tables)).
@@ -138,66 +126,66 @@ field(Tables) ->
split(_, Fs, Vs) ->
split(Fs, Vs).
-%%% ----------------------------------------------------------
-%%% # modules()
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # modules()
+%% ----------------------------------------------------------
modules() ->
- Path = filename:join([appdir(), atom_to_list(?APPLICATION) ++ ".app"]),
- {ok, [{application, ?APPLICATION, Attrs}]} = file:consult(Path),
+ Path = filename:join([appdir(), atom_to_list(?APP) ++ ".app"]),
+ {ok, [{application, ?APP, Attrs}]} = file:consult(Path),
{modules, Mods} = lists:keyfind(modules, 1, Attrs),
Mods.
appdir() ->
- [_|_] = code:lib_dir(?APPLICATION, ebin).
+ [_|_] = code:lib_dir(?APP, ebin).
-%%% ----------------------------------------------------------
-%%% # versions()
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # versions()
+%% ----------------------------------------------------------
versions() ->
- ?INFO:versions(modules()).
+ ?I:versions(modules()).
-%%% ----------------------------------------------------------
-%%% # versions()
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # versions()
+%% ----------------------------------------------------------
version_info() ->
- ?INFO:version_info(modules()).
+ ?I:version_info(modules()).
-%%% ----------------------------------------------------------
-%%% # compiled()
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # compiled()
+%% ----------------------------------------------------------
compiled() ->
- ?INFO:compiled(modules()).
+ ?I:compiled(modules()).
-%%% ----------------------------------------------------------
-%%% procs()
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% procs()
+%% ----------------------------------------------------------
procs() ->
- ?INFO:procs(?APPLICATION).
+ ?I:procs(?APP).
-%%% ----------------------------------------------------------
-%%% # latest()
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # latest()
+%% ----------------------------------------------------------
latest() ->
- ?INFO:latest(modules()).
+ ?I:latest(modules()).
-%%% ----------------------------------------------------------
-%%% # nl()
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # nl()
+%% ----------------------------------------------------------
nl() ->
lists:foreach(fun(M) -> abcast = c:nl(M) end, modules()).
-%%% ----------------------------------------------------------
-%%% # pp(Bin)
-%%%
-%%% Description: Pretty-print a message binary.
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # pp(Bin)
+%%
+%% Description: Pretty-print a message binary.
+%% ----------------------------------------------------------
%% Network byte order = big endian.
@@ -207,7 +195,7 @@ pp(<<Version:8, MsgLength:24,
HbHid:32,
E2Eid:32,
AVPs/binary>>) ->
- ?SEP(),
+ ?I:sep(),
ppp(["Version",
"Message length",
"[Actual length]",
@@ -227,7 +215,7 @@ pp(<<Version:8, MsgLength:24,
HbHid,
E2Eid]),
N = avp_loop({AVPs, MsgLength - 20}, 0),
- ?SEP(),
+ ?I:sep(),
N;
pp(<<_Version:8, MsgLength:24, _/binary>> = Bin) ->
@@ -328,23 +316,23 @@ ppp(Fields, Values) ->
ppp({Field, Value}) ->
io:format(": ~-22s : ~p~n", [Field, Value]).
-%%% ----------------------------------------------------------
-%%% # subscriptions()
-%%%
-%%% Output: list of {SvcName, Pid}
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # subscriptions()
+%%
+%% Returns a list of {SvcName, Pid}.
+%% ----------------------------------------------------------
subscriptions() ->
diameter_service:subscriptions().
-%%% ----------------------------------------------------------
-%%% # children()
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
+%% # children()
+%% ----------------------------------------------------------
children() ->
diameter_sup:tree().
-%%% ----------------------------------------------------------
+%% ----------------------------------------------------------
%% tracer/[12]
@@ -430,7 +418,7 @@ peers(Name, Ts) ->
mk_peers(Name, [_, {type, connect} | _] = Ts) ->
[[Name | mk_peer(Ts)]];
-mk_peers(Name, [R, {type, listen}, O, {accept = A, As}]) ->
+mk_peers(Name, [R, {type, listen}, O, {accept = A, As} | _]) ->
[[Name | mk_peer([R, {type, A}, O | Ts])] || Ts <- As].
%% This is a bit lame: service_info works to build this list and out
%% of something like what we want here and then we take it apart.
@@ -485,13 +473,12 @@ fields(diameter_service) ->
[started,
name,
record_info(fields, diameter_service),
+ watchdogT,
peerT,
- connT,
- share_peers,
- use_shared_peers,
shared_peers,
local_peers,
- monitor];
+ monitor,
+ options];
?FIELDS(diameter_event);
?FIELDS(diameter_uri);
diff --git a/lib/diameter/src/base/diameter_info.erl b/lib/diameter/src/info/diameter_info.erl
index 39d32d07cd..10972f3231 100644
--- a/lib/diameter/src/base/diameter_info.erl
+++ b/lib/diameter/src/info/diameter_info.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,6 +17,11 @@
%% %CopyrightEnd%
%%
+%%
+%% Generic functions for formatting table listings and more. Used by
+%% diameter_dbg.
+%%
+
-module(diameter_info).
-export([usage/1,
@@ -573,12 +578,7 @@ sys_info() ->
{A,V}.
os_info() ->
- {os:version(), case os:type() of
- {_Fam, _Name} = T ->
- T;
- Fam ->
- {Fam, ""}
- end}.
+ {os:version(), os:type()}.
chomp(S) ->
string:strip(S, right, $\n).
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
index f8d3cf1d6f..a2a7a51892 100644
--- a/lib/diameter/src/modules.mk
+++ b/lib/diameter/src/modules.mk
@@ -1,8 +1,7 @@
-#-*-makefile-*- ; force emacs to enter makefile-mode
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2013. All Rights Reserved.
+# Copyright Ericsson AB 2010-2014. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -64,16 +63,19 @@ RT_MODULES = \
transport/diameter_transport \
transport/diameter_transport_sup
-# Handwritten (compile time) modules not included in the app file.
+# Handwritten compiler modules not included in the app file.
CT_MODULES = \
- base/diameter_dbg \
- base/diameter_info \
compiler/diameter_codegen \
compiler/diameter_exprecs \
compiler/diameter_dict_scanner \
compiler/diameter_dict_util \
compiler/diameter_make
+# Info/debug modules, also not included in the app file.
+INFO_MODULES = \
+ info/diameter_dbg \
+ info/diameter_info
+
# Released hrl files in ../include intended for public consumption.
EXTERNAL_HRLS = \
diameter.hrl \
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index d0a01351f3..32e7aaca39 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -616,6 +616,8 @@ send(#diameter_packet{bin = Bin, transport_data = {outstream, SId}},
S;
%% ... or not: rotate through all streams.
+send(#diameter_packet{bin = Bin}, S) ->
+ send(Bin, S);
send(Bin, #transport{streams = {_, OS},
os = N}
= S)
diff --git a/lib/diameter/test/diameter_app_SUITE.erl b/lib/diameter/test/diameter_app_SUITE.erl
index 1e262895a6..f68a18b5c2 100644
--- a/lib/diameter/test/diameter_app_SUITE.erl
+++ b/lib/diameter/test/diameter_app_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -50,7 +50,7 @@
diameter_exprecs,
diameter_make]).
--define(HELP_MODULES, [diameter_dbg,
+-define(INFO_MODULES, [diameter_dbg,
diameter_info]).
%% ===========================================================================
@@ -99,13 +99,13 @@ vsn(Config) ->
%% # modules/1
%%
%% Ensure that the app file modules and installed modules differ by
-%% compiler/help modules.
+%% compiler/info modules.
%% ===========================================================================
modules(Config) ->
Mods = fetch(modules, fetch(app, Config)),
Installed = code_mods(),
- Help = lists:sort(?HELP_MODULES ++ ?COMPILER_MODULES),
+ Help = lists:sort(?INFO_MODULES ++ ?COMPILER_MODULES),
{[], Help} = {Mods -- Installed, lists:sort(Installed -- Mods)}.
@@ -158,12 +158,15 @@ appvsn(Name) ->
%% # xref/1
%%
%% Ensure that no function in our application calls an undefined function
-%% or one in an application we haven't specified as a dependency. (Almost.)
+%% or one in an application we haven't declared as a dependency. (Almost.)
%% ===========================================================================
xref(Config) ->
App = fetch(app, Config),
- Mods = fetch(modules, App),
+ Mods = fetch(modules, App), %% modules listed in the app file
+
+ %% List of application names extracted from runtime_dependencies.
+ Deps = lists:map(fun unversion/1, fetch(runtime_dependencies, App)),
{ok, XRef} = xref:start(make_name(xref_test_name)),
ok = xref:set_default(XRef, [{verbose, false}, {warnings, false}]),
@@ -178,7 +181,9 @@ xref(Config) ->
[?APP, erts | fetch(applications, App)]),
{ok, Undefs} = xref:analyze(XRef, undefined_function_calls),
- {ok, Called} = xref:analyze(XRef, {module_call, ?COMPILER_MODULES}),
+ {ok, RTmods} = xref:analyze(XRef, {module_use, Mods}),
+ {ok, CTmods} = xref:analyze(XRef, {module_use, ?COMPILER_MODULES}),
+ {ok, RTdeps} = xref:analyze(XRef, {module_call, Mods}),
xref:stop(XRef),
@@ -190,18 +195,41 @@ xref(Config) ->
Undefs),
%% diameter_tcp does call ssl despite the latter not being listed
%% as a dependency in the app file since ssl is only required for
- %% TLS security: it's up to a client who wants TLS it to start
- %% ssl.
-
- [] = lists:filter(fun is_bad_dependency/1, Called).
-
-%% It's not strictly necessary that diameter compiler modules not
-%% depend on other diameter modules but it's a simple source of build
-%% errors if not encoded in the makefile (hence the test) so guard
-%% against it.
-is_bad_dependency(Mod) ->
- lists:prefix("diameter", atom_to_list(Mod))
- andalso not lists:member(Mod, ?COMPILER_MODULES).
+ %% TLS security: it's up to a client who wants TLS to start ssl.
+
+ %% Ensure that only runtime or info modules call runtime modules.
+ %% It's not strictly necessary that diameter compiler modules not
+ %% depend on other diameter modules but it's a simple source of
+ %% build errors if not properly encoded in the makefile so guard
+ %% against it.
+ [] = (RTmods -- Mods) -- ?INFO_MODULES,
+
+ %% Ensure that runtime modules don't call compiler modules.
+ CTmods = CTmods -- Mods,
+
+ %% Ensure that runtime modules only call other runtime modules, or
+ %% applications declared as in runtime_dependencies in the app
+ %% file. Note that the declared application versions are ignored
+ %% since we only know what we can see now.
+ [] = lists:filter(fun(M) -> not lists:member(app(M), Deps) end,
+ RTdeps -- Mods).
+
+unversion(App) ->
+ T = lists:dropwhile(fun is_vsn_ch/1, lists:reverse(App)),
+ lists:reverse(case T of [$-|TT] -> TT; _ -> T end).
+
+is_vsn_ch(C) ->
+ $0 =< C andalso C =< $9 orelse $. == C.
+
+app('$M_EXPR') -> %% could be anything but assume it's ok
+ "erts";
+app(Mod) ->
+ case code:which(Mod) of
+ preloaded ->
+ "erts";
+ Path ->
+ unversion(lists:nth(3, lists:reverse(filename:split(Path))))
+ end.
add_application(XRef, App) ->
add_application(XRef, App, code:lib_dir(App)).
diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl
index 0b4568a9e5..90536dcf2b 100644
--- a/lib/diameter/test/diameter_codec_test.erl
+++ b/lib/diameter/test/diameter_codec_test.erl
@@ -1,8 +1,7 @@
-%% coding: utf-8
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/diameter/test/diameter_compiler_SUITE.erl b/lib/diameter/test/diameter_compiler_SUITE.erl
index df4dde6240..20c9275808 100644
--- a/lib/diameter/test/diameter_compiler_SUITE.erl
+++ b/lib/diameter/test/diameter_compiler_SUITE.erl
@@ -120,6 +120,16 @@
{avp_has_duplicate_flag,
" -",
" MM"},
+ {ok,
+ "@vendor 0",
+ "@vendor 10415"},
+ {ok,
+ [{"@vendor 0", "@vendor 10415"},
+ {"Proxy-Info .*M$", "&V"},
+ {"Proxy-Info ::= [^>]*", "& 10415 "}]},
+ {grouped_vendor_id_without_flag,
+ [{"@vendor 0", "@vendor 10415"},
+ {"Proxy-Info ::= [^>]*", "& 10415 "}]},
{avp_has_vendor_id,
"@avp_types",
"@avp_vendor_id 667 Class\n&"},
@@ -307,6 +317,21 @@
{avp_not_defined,
"CEA ::=",
"<XXX> &"},
+ {ok,
+ "@avp_types",
+ "@codecs tmod Session-Id &"},
+ {ok,
+ "@avp_types",
+ "@custom_types tmod Session-Id &"},
+ {avp_not_defined,
+ "@avp_types",
+ "@codecs tmod OctetString &"},
+ {avp_not_defined,
+ "@avp_types",
+ "@custom_types tmod OctetString &"},
+ {avp_already_defined,
+ "@avp_types",
+ "@codecs tmod Session-Id @custom_types tmod Session-Id &"},
{not_loaded,
[{"@avp_types", "@inherits nomod XXX &"},
{"CEA ::=", "<XXX> &"}]},
diff --git a/lib/diameter/test/diameter_dpr_SUITE.erl b/lib/diameter/test/diameter_dpr_SUITE.erl
index 9252650bf7..f3f16b06e0 100644
--- a/lib/diameter/test/diameter_dpr_SUITE.erl
+++ b/lib/diameter/test/diameter_dpr_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -73,7 +73,7 @@
%% Valid values for Disconnect-Cause.
-define(CAUSES, [0, rebooting, 1, busy, 2, goaway]).
-%% Establish one client connection for element of this list,
+%% Establish one client connection for each element of this list,
%% configured with disconnect/5 as disconnect_cb and returning the
%% specified value.
-define(RETURNS,
@@ -129,8 +129,8 @@ stop_service(Config) ->
service == group(Config)
andalso (ok = diameter:stop_service(?CLIENT)).
-%% Check for callbacks and stop the service. (Not the other way around
-%% for the timing reason explained below.)
+%% Check for callbacks before diameter:stop/0, not the other way around
+%% for the timing reason explained below.
check(Config) ->
Grp = group(Config),
[Pid | Refs] = ?util:read_priv(Config, config),
diff --git a/lib/diameter/test/diameter_examples_SUITE.erl b/lib/diameter/test/diameter_examples_SUITE.erl
index 02c8d34361..aef4bc35ef 100644
--- a/lib/diameter/test/diameter_examples_SUITE.erl
+++ b/lib/diameter/test/diameter_examples_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,7 +24,10 @@
-module(diameter_examples_SUITE).
-export([suite/0,
- all/0]).
+ all/0,
+ groups/0,
+ init_per_group/2,
+ end_per_group/2]).
%% testcases
-export([dict/1, dict/0,
@@ -46,7 +49,7 @@
%% The order here is significant and causes the server to listen
%% before the clients connect.
--define(NODES, [compile, server, client]).
+-define(NODES, [server, client]).
%% Options to ct_slave:start/2.
-define(TIMEOUTS, [{T, 15000} || T <- [boot_timeout,
@@ -63,6 +66,9 @@
%% Common dictionaries to inherit from examples.
-define(DICT0, [rfc3588_base, rfc6733_base]).
+%% Transport protocols over which the example Diameter nodes are run.
+-define(PROTS, [tcp, sctp]).
+
%% ===========================================================================
suite() ->
@@ -71,7 +77,34 @@ suite() ->
all() ->
[dict,
code,
- slave,
+ {group, all}].
+
+groups() ->
+ Tc = tc(),
+ [{all, [parallel], [{group, P} || P <- ?PROTS]}
+ | [{P, [], Tc} || P <- ?PROTS]].
+
+init_per_group(all, Config) ->
+ Config;
+
+init_per_group(tcp = N, Config) ->
+ [{group, N} | Config];
+
+init_per_group(sctp = N, Config) ->
+ case gen_sctp:open() of
+ {ok, Sock} ->
+ gen_sctp:close(Sock),
+ [{group, N} | Config];
+ {error, E} when E == eprotonosupport;
+ E == esocktnosupport -> %% fail on any other reason
+ {skip, no_sctp}
+ end.
+
+end_per_group(_, _) ->
+ ok.
+
+tc() ->
+ [slave,
enslave,
start,
traffic,
@@ -88,7 +121,7 @@ dict() ->
dict(_Config) ->
Dirs = [filename:join(H ++ ["examples", "dict"])
|| H <- [[code:lib_dir(diameter)], [here(), ".."]]],
- [] = [{F,D,RC} || {_,F} <- sort(find_files(Dirs, ".*\\.dia")),
+ [] = [{F,D,RC} || {_,F} <- sort(find_files(Dirs, ".*\\.dia$")),
D <- ?DICT0,
RC <- [make(F,D)],
RC /= ok].
@@ -184,17 +217,18 @@ make_name(Dict) ->
%% Compile example code under examples/code.
code(Config) ->
- Node = slave(hd(?NODES), here()),
+ Node = slave(compile, here()),
[] = rpc:call(Node,
?MODULE,
install,
- [proplists:get_value(priv_dir, Config)]).
+ [proplists:get_value(priv_dir, Config)]),
+ {ok, Node} = ct_slave:stop(compile).
%% Compile on another node since the code path may be modified.
install(PrivDir) ->
Top = install(here(), PrivDir),
Src = filename:join([Top, "examples", "code"]),
- Files = find_files([Src], ".*\\.erl"),
+ Files = find_files([Src], ".*\\.erl$"),
[] = [{F,E} || {_,F} <- Files,
{error, _, _} = E <- [compile:file(F, [warnings_as_errors,
return_errors])]].
@@ -226,7 +260,7 @@ install(Dir, PrivDir) ->
Inc = filename:join([Top, "include"]),
Gen = filename:join([Top, "src", "gen"]),
- Files = find_files([Inc, Gen], ".*\\.hrl"),
+ Files = find_files([Inc, Gen], ".*\\.hrl$"),
[] = [{F,E} || {_,F} <- Files,
B <- [filename:basename(F)],
D <- [filename:join([TmpInc, B])],
@@ -280,9 +314,10 @@ now_diff(_) ->
%% Start two nodes: one for the server, one for the client.
enslave(Config) ->
+ Prot = proplists:get_value(group, Config),
Dir = here(),
- Nodes = [{N, slave(N, Dir)} || N <- tl(?NODES)],
- ?util:write_priv(Config, nodes, Nodes).
+ Nodes = [{S, slave(N, Dir)} || S <- ?NODES, N <- [concat(Prot, S)]],
+ ?util:write_priv(Config, Prot, Nodes).
slave(Name, Dir) ->
{ok, Node} = ct_slave:start(Name, ?TIMEOUTS),
@@ -292,6 +327,9 @@ slave(Name, Dir) ->
[[Dir, filename:join([Dir, "..", "ebin"])]]),
Node.
+concat(Prot, Svc) ->
+ list_to_atom(atom_to_list(Prot) ++ atom_to_list(Svc)).
+
here() ->
filename:dirname(code:which(?MODULE)).
@@ -304,24 +342,25 @@ top(Dir, LibDir) ->
%% start/1
-start(server) ->
+start({server, Prot}) ->
ok = diameter:start(),
ok = server:start(),
- {ok, Ref} = server:listen(tcp),
- [_] = ?util:lport(tcp, Ref),
+ {ok, Ref} = server:listen(Prot),
+ [_] = ?util:lport(Prot, Ref),
ok;
-start(client) ->
+start({client = Svc, Prot}) ->
ok = diameter:start(),
- true = diameter:subscribe(client),
+ true = diameter:subscribe(Svc),
ok = client:start(),
- {ok, Ref} = client:connect(tcp),
+ {ok, Ref} = client:connect(Prot),
receive #diameter_event{info = {up, Ref, _, _, _}} -> ok end;
start(Config) ->
- Nodes = ?util:read_priv(Config, nodes),
+ Prot = proplists:get_value(group, Config),
+ Nodes = ?util:read_priv(Config, Prot),
[] = [RC || {T,N} <- Nodes,
- RC <- [rpc:call(N, ?MODULE, start, [T])],
+ RC <- [rpc:call(N, ?MODULE, start, [{T, Prot}])],
RC /= ok].
%% traffic/1
@@ -336,7 +375,8 @@ traffic(client) ->
receive {'DOWN', MRef, process, _, Reason} -> Reason end;
traffic(Config) ->
- Nodes = ?util:read_priv(Config, nodes),
+ Prot = proplists:get_value(group, Config),
+ Nodes = ?util:read_priv(Config, Prot),
[] = [RC || {T,N} <- Nodes,
RC <- [rpc:call(N, ?MODULE, traffic, [T])],
RC /= ok].
@@ -355,5 +395,6 @@ stop(Name)
{ok, _Node} = ct_slave:stop(Name),
ok;
-stop(_Config) ->
- [] = [RC || N <- ?NODES, RC <- [stop(N)], RC /= ok].
+stop(Config) ->
+ Prot = proplists:get_value(group, Config),
+ [] = [RC || N <- ?NODES, RC <- [stop(concat(Prot, N))], RC /= ok].
diff --git a/lib/diameter/test/diameter_failover_SUITE.erl b/lib/diameter/test/diameter_failover_SUITE.erl
index dfd3253827..c1494dcdb1 100644
--- a/lib/diameter/test/diameter_failover_SUITE.erl
+++ b/lib/diameter/test/diameter_failover_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -47,6 +47,7 @@
send_discard_1/1,
send_discard_2/1,
stop_services/1,
+ empty/1,
stop/1]).
%% diameter callbacks
@@ -121,6 +122,7 @@ all() ->
send_discard_1,
send_discard_2,
stop_services,
+ empty,
stop].
%% ===========================================================================
@@ -147,6 +149,10 @@ stop_services(_Config) ->
T <- [diameter:stop_service(H)],
T /= ok].
+%% Ensure transports have been removed from request table.
+empty(_Config) ->
+ [] = ets:tab2list(diameter_request).
+
stop(_Config) ->
ok = diameter:stop().
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index a97c54fc04..4b67372016 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -43,7 +43,9 @@
send_protocol_error/1,
send_arbitrary/1,
send_unknown/1,
+ send_unknown_short/1,
send_unknown_mandatory/1,
+ send_unknown_short_mandatory/1,
send_noreply/1,
send_unsupported/1,
send_unsupported_app/1,
@@ -54,7 +56,8 @@
send_zero_avp_length/1,
send_invalid_avp_length/1,
send_invalid_reject/1,
- send_unrecognized_mandatory/1,
+ send_unexpected_mandatory_decode/1,
+ send_unexpected_mandatory/1,
send_long/1,
send_nopeer/1,
send_noapp/1,
@@ -266,7 +269,9 @@ tc() ->
send_protocol_error,
send_arbitrary,
send_unknown,
+ send_unknown_short,
send_unknown_mandatory,
+ send_unknown_short_mandatory,
send_noreply,
send_unsupported,
send_unsupported_app,
@@ -277,7 +282,8 @@ tc() ->
send_zero_avp_length,
send_invalid_avp_length,
send_invalid_reject,
- send_unrecognized_mandatory,
+ send_unexpected_mandatory_decode,
+ send_unexpected_mandatory,
send_long,
send_nopeer,
send_noapp,
@@ -447,6 +453,24 @@ send_unknown(Config) ->
data = <<17>>}]}
= lists:last(Avps).
+%% Ditto, and point the AVP length past the end of the message. Expect
+%% 5014.
+send_unknown_short(Config) ->
+ send_unknown_short(Config, false, ?INVALID_AVP_LENGTH).
+
+send_unknown_short(Config, M, RC) ->
+ Req = ['ASR', {'AVP', [#diameter_avp{code = 999,
+ is_mandatory = M,
+ data = <<17>>}]}],
+ ['ASA', _SessionId, {'Result-Code', RC} | Avps]
+ = call(Config, Req),
+ [#'diameter_base_Failed-AVP'{'AVP' = As}]
+ = proplists:get_value('Failed-AVP', Avps),
+ [#diameter_avp{code = 999,
+ is_mandatory = M,
+ data = <<17, _/binary>>}] %% extra bits from padding
+ = As.
+
%% Ditto but set the M flag.
send_unknown_mandatory(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{code = 999,
@@ -461,6 +485,27 @@ send_unknown_mandatory(Config) ->
data = <<17>>}]
= As.
+%% Ditto, and point the AVP length past the end of the message. Expect
+%% 5014 instead of 5001.
+send_unknown_short_mandatory(Config) ->
+ send_unknown_short(Config, true, ?INVALID_AVP_LENGTH).
+
+%% Send an ACR containing an unexpected mandatory Session-Timeout.
+%% Expect 5001, and check that the value in Failed-AVP was decoded.
+send_unexpected_mandatory_decode(Config) ->
+ Req = ['ASR', {'AVP', [#diameter_avp{code = 27, %% Session-Timeout
+ is_mandatory = true,
+ data = <<12:32>>}]}],
+ ['ASA', _SessionId, {'Result-Code', ?AVP_UNSUPPORTED} | Avps]
+ = call(Config, Req),
+ [#'diameter_base_Failed-AVP'{'AVP' = As}]
+ = proplists:get_value('Failed-AVP', Avps),
+ [#diameter_avp{code = 27,
+ is_mandatory = true,
+ value = 12,
+ data = <<12:32>>}]
+ = As.
+
%% Send an STR that the server ignores.
send_noreply(Config) ->
Req = ['STR', {'Termination-Cause', ?BAD_ANSWER}],
@@ -527,9 +572,9 @@ send_invalid_reject(Config) ->
?answer_message(?TOO_BUSY)
= call(Config, Req).
-%% Send an STR containing a known AVP, but one that's not allowed and
-%% sets the M-bit.
-send_unrecognized_mandatory(Config) ->
+%% Send an STR containing a known AVP, but one that's not expected and
+%% that sets the M-bit.
+send_unexpected_mandatory(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
['STA', _SessionId, {'Result-Code', ?AVP_UNSUPPORTED} | _]
@@ -836,6 +881,26 @@ log(#diameter_packet{bin = Bin} = P, T)
%% prepare/4
prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group)
+ when N == send_unknown_short_mandatory;
+ N == send_unknown_short ->
+ Req = prepare(Pkt, Caps, Group),
+
+ #diameter_packet{header = #diameter_header{length = L},
+ bin = Bin}
+ = E
+ = diameter_codec:encode(Dict0, Pkt#diameter_packet{msg = Req}),
+
+ %% Find the unknown AVP data at the end of the message and alter
+ %% its length header.
+
+ {Padding, [17|_]} = lists:splitwith(fun(C) -> C == 0 end,
+ lists:reverse(binary_to_list(Bin))),
+
+ Offset = L - length(Padding) - 4,
+ <<H:Offset/binary, Len:24, T/binary>> = Bin,
+ E#diameter_packet{bin = <<H/binary, (Len+9):24, T/binary>>};
+
+prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group)
when N == send_long_avp_length;
N == send_short_avp_length;
N == send_zero_avp_length ->
@@ -876,8 +941,8 @@ prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group)
<<V, L:24, H/binary>> = H0, %% assert
E#diameter_packet{bin = <<V, (L+4):24, H/binary, 16:24, 0:32, T/binary>>};
-prepare(Pkt, Caps, send_unrecognized_mandatory, #group{client_dict0 = Dict0}
- = Group) ->
+prepare(Pkt, Caps, send_unexpected_mandatory, #group{client_dict0 = Dict0}
+ = Group) ->
Req = prepare(Pkt, Caps, Group),
#diameter_packet{bin = <<V, Len:24, T/binary>>}
= E
@@ -997,7 +1062,9 @@ answer(Rec, [_|_], N)
N == send_short_avp_length;
N == send_zero_avp_length;
N == send_invalid_avp_length;
- N == send_invalid_reject ->
+ N == send_invalid_reject;
+ N == send_unknown_short_mandatory;
+ N == send_unexpected_mandatory_decode ->
Rec;
answer(Rec, [], _) ->
Rec.
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index 54019fa46c..560c2aed50 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -18,5 +18,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.6
+DIAMETER_VSN = 1.7
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml
index 394e7af09c..d7cbfa1fdc 100644
--- a/lib/edoc/doc/src/notes.xml
+++ b/lib/edoc/doc/src/notes.xml
@@ -31,6 +31,35 @@
<p>This document describes the changes made to the EDoc
application.</p>
+<section><title>Edoc 0.7.13</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Edoc 0.7.12.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/edoc/src/edoc.app.src b/lib/edoc/src/edoc.app.src
index 0c8d5b85f8..9e1155d3e8 100644
--- a/lib/edoc/src/edoc.app.src
+++ b/lib/edoc/src/edoc.app.src
@@ -22,4 +22,6 @@
otpsgml_layout]},
{registered,[]},
{applications, [compiler,kernel,stdlib,syntax_tools]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["xmerl-1.3.7","syntax_tools-1.6.14","stdlib-2.0",
+ "kernel-3.0","inets-5.10","erts-6.0"]}]}.
diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl
index af8f1230fb..d4e00d3ecd 100644
--- a/lib/edoc/src/edoc_types.erl
+++ b/lib/edoc/src/edoc_types.erl
@@ -66,6 +66,7 @@ is_new_predefined(boolean, 0) -> true;
is_new_predefined(byte, 0) -> true;
is_new_predefined(iodata, 0) -> true;
is_new_predefined(iolist, 0) -> true;
+is_new_predefined(map, 0) -> true;
is_new_predefined(maybe_improper_list, 0) -> true;
is_new_predefined(maybe_improper_list, 2) -> true;
is_new_predefined(mfa, 0) -> true;
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index 2fcc97e406..0172aac48b 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.7.12.1
+EDOC_VSN = 0.7.13
diff --git a/lib/eldap/doc/src/notes.xml b/lib/eldap/doc/src/notes.xml
index e7a3e20202..089bb731d4 100644
--- a/lib/eldap/doc/src/notes.xml
+++ b/lib/eldap/doc/src/notes.xml
@@ -30,7 +30,51 @@
</header>
<p>This document describes the changes made to the Eldap application.</p>
- <section><title>Eldap 1.0.2</title>
+ <section><title>Eldap 1.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ <item>
+ <p>
+ Add support for IPv6 connections, By including the
+ [inet6] option in eldap:open/2. Default value is still
+ [inet] (Thanks to Edwin Fine)</p>
+ <p>
+ Own Id: OTP-11753</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug where eldap:search returned binaries instead of
+ strings. (Thanks Simon MacMullen for the report)</p>
+ <p>
+ Own Id: OTP-11768</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Eldap 1.0.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/eldap/src/Makefile b/lib/eldap/src/Makefile
index ebb7967e11..2e1110ec2c 100644
--- a/lib/eldap/src/Makefile
+++ b/lib/eldap/src/Makefile
@@ -88,7 +88,7 @@ $(TARGET_FILES): $(HRL_FILES)
# Special Build Targets
# ----------------------------------------------------
$(ASN1_HRL): ../asn1/$(ASN1_FILES)
- $(asn_verbose)$(ERLC) -o $(EBIN) -bber $(ERL_COMPILE_FLAGS) ../asn1/ELDAPv3.asn1
+ $(asn_verbose)$(ERLC) -o $(EBIN) +legacy_erlang_types -bber $(ERL_COMPILE_FLAGS) ../asn1/ELDAPv3.asn1
# ----------------------------------------------------
# Release Target
diff --git a/lib/eldap/src/eldap.app.src b/lib/eldap/src/eldap.app.src
index 8215328910..03a7d7c562 100644
--- a/lib/eldap/src/eldap.app.src
+++ b/lib/eldap/src/eldap.app.src
@@ -4,5 +4,7 @@
{modules, [eldap, 'ELDAPv3']},
{registered, []},
{applications, [kernel, stdlib]},
- {env, []}
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","kernel-3.0","erts-6.0",
+ "asn1-3.0"]}
]}.
diff --git a/lib/eldap/test/eldap_misc_SUITE.erl b/lib/eldap/test/eldap_misc_SUITE.erl
new file mode 100644
index 0000000000..ca810ee33c
--- /dev/null
+++ b/lib/eldap/test/eldap_misc_SUITE.erl
@@ -0,0 +1,51 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012-2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(eldap_misc_SUITE).
+
+-compile(export_all). %% Use this only in test suites...
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eldap/include/eldap.hrl").
+-include_lib("eldap/ebin/ELDAPv3.hrl").
+
+all() ->
+ [
+ encode,
+ decode
+ ].
+
+
+encode(_Config) ->
+ {ok,Bin} = 'ELDAPv3':encode('AddRequest', #'AddRequest'{entry="hejHopp" ,attributes=[]} ),
+ Expected = <<104,11,4,7,104,101,106,72,111,112,112,48,0>>,
+ Expected = Bin.
+
+decode(_Config) ->
+ {ok,Res} = 'ELDAPv3':decode('AddRequest', <<104,11,4,7,104,101,106,72,111,112,112,48,0>>),
+ ct:log("Res = ~p", [Res]),
+ Expected = #'AddRequest'{entry = "hejHopp",attributes = []},
+ case Res of
+ Expected -> ok;
+ #'AddRequest'{entry= <<"hejHopp">>, attributes=[]} ->
+ {fail, "decoded to (correct) binary!!"};
+ _ ->
+ {fail, "Bad decode"}
+ end.
+
diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml
index 24d12b2bb7..e546eb97fc 100644
--- a/lib/erl_docgen/doc/src/notes.xml
+++ b/lib/erl_docgen/doc/src/notes.xml
@@ -30,7 +30,48 @@
</header>
<p>This document describes the changes made to the <em>erl_docgen</em> application.</p>
- <section><title>Erl_Docgen 0.3.4.1</title>
+ <section><title>Erl_Docgen 0.3.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Handle map types in docgen_edoc_xml_cb</p>
+ <p>
+ Own Id: OTP-11776</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.3.4.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
index e3cc354206..e6bf9ce20e 100644
--- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
+++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
@@ -1014,6 +1014,8 @@ t_type([#xmlElement{name = nil}]) ->
t_nil();
t_type([#xmlElement{name = list, content = Es}]) ->
t_list(Es);
+t_type([#xmlElement{name = nonempty_list, content = Es}]) ->
+ t_nonempty_list(Es);
t_type([#xmlElement{name = tuple, content = Es}]) ->
t_tuple(Es);
t_type([#xmlElement{name = 'fun', content = Es}]) ->
@@ -1023,7 +1025,11 @@ t_type([#xmlElement{name = abstype, content = Es}]) ->
t_type([#xmlElement{name = union, content = Es}]) ->
t_union(Es);
t_type([#xmlElement{name = record, content = Es}]) ->
- t_record(Es).
+ t_record(Es);
+t_type([#xmlElement{name = map, content = Es}]) ->
+ t_map(Es);
+t_type([#xmlElement{name = map_field, content = Es}]) ->
+ t_map_field(Es).
t_var(E) ->
[get_attrval(name, E)].
@@ -1046,6 +1052,9 @@ t_nil() ->
t_list(Es) ->
["["] ++ t_utype(get_elem(type, Es)) ++ ["]"].
+t_nonempty_list(Es) ->
+ ["["] ++ t_utype(get_elem(type, Es)) ++ [", ...]"].
+
t_tuple(Es) ->
["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
@@ -1058,6 +1067,12 @@ t_record([E|Es]) ->
t_field(#xmlElement{name=field, content=[Atom,Type]}) ->
[get_attrval(value, Atom), "="] ++ t_utype_elem(Type).
+t_map(Es) ->
+ ["#{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+
+t_map_field([K,V]) ->
+ [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)].
+
t_abstype(Es) ->
case split_at_colon(t_name(get_elem(erlangName, Es)),[]) of
{Mod,Type} ->
diff --git a/lib/erl_docgen/src/docgen_otp_specs.erl b/lib/erl_docgen/src/docgen_otp_specs.erl
index 3240edd68e..886194598f 100644
--- a/lib/erl_docgen/src/docgen_otp_specs.erl
+++ b/lib/erl_docgen/src/docgen_otp_specs.erl
@@ -433,7 +433,7 @@ t_tuple(Es) ->
["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
t_map() ->
- ["#{}"].
+ ["map()"].
t_fun(Es) ->
["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
diff --git a/lib/erl_docgen/src/erl_docgen.app.src b/lib/erl_docgen/src/erl_docgen.app.src
index daad172106..e2830b2692 100644
--- a/lib/erl_docgen/src/erl_docgen.app.src
+++ b/lib/erl_docgen/src/erl_docgen.app.src
@@ -8,7 +8,7 @@
},
{registered,[]},
{applications, [kernel,stdlib]},
- {env, []
- }
+ {env, []},
+ {runtime_dependencies, ["xmerl-1.3.7","stdlib-2.0","edoc-0.7.13","erts-6.0"]}
]
}.
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index cda8671cfd..0f89922275 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1 +1 @@
-ERL_DOCGEN_VSN = 0.3.4.1
+ERL_DOCGEN_VSN = 0.3.5
diff --git a/lib/erl_interface/aclocal.m4 b/lib/erl_interface/aclocal.m4
index c51c26794a..ed492d55ff 100644
--- a/lib/erl_interface/aclocal.m4
+++ b/lib/erl_interface/aclocal.m4
@@ -83,8 +83,10 @@ AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross
AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_LM_CONF, [OSE load module default configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE OSE confd source file])
+AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file])
AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file])
])
@@ -1116,7 +1118,7 @@ case "$THR_LIB_NAME" in
[Define if you have the "ose_spi/ose_spi.h" header file.]))
;;
esac
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
case $host_os in
openbsd*)
# The default stack size is insufficient for our needs
@@ -1220,7 +1222,7 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for functions
dnl
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
AC_CHECK_FUNC(pthread_spin_lock, \
[ethr_have_native_spinlock=yes \
AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml
index ab185c9179..90495eebd6 100644
--- a/lib/erl_interface/doc/src/ei.xml
+++ b/lib/erl_interface/doc/src/ei.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2013</year>
+ <year>2001</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -417,6 +417,26 @@ ei_x_encode_empty_list(&amp;x);
</desc>
</func>
<func>
+ <name><ret>int</ret><nametext>ei_encode_map_header(char *buf, int *index, int arity)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_map_header(ei_x_buff* x, int arity)</nametext></name>
+ <fsummary>Encode a map</fsummary>
+ <desc>
+ <p>This function encodes a map header, with a specified arity. The next
+ <c>arity*2</c> terms encoded will be the keys and values of the map
+ encoded in the following order: <c>K1, V1, K2, V2, ..., Kn, Vn</c>.
+ </p>
+ <p>E.g. to encode the map <c>#{a => "Apple", b => "Banana"}</c>:</p>
+ <pre>
+ei_x_encode_map_header(&amp;x, 2);
+ei_x_encode_atom(&amp;x, "a");
+ei_x_encode_string(&amp;x, "Apple");
+ei_x_encode_atom(&amp;x, "b");
+ei_x_encode_string(&amp;x, "Banana");
+ </pre>
+ <p>A correctly encoded map can not have duplicate keys.</p>
+ </desc>
+ </func>
+ <func>
<name><ret>int</ret><nametext>ei_get_type(const char *buf, const int *index, int *type, int *size)</nametext></name>
<fsummary>Fetch the type and size of an encoded term</fsummary>
<desc>
@@ -638,6 +658,18 @@ ei_x_encode_empty_list(&amp;x);
</desc>
</func>
<func>
+ <name><ret>int</ret><nametext>ei_decode_map_header(const char *buf, int *index, int *arity)</nametext></name>
+ <fsummary>Decode a map</fsummary>
+ <desc>
+ <p>This function decodes a map header from the binary
+ format. The number of key-value pairs is returned in
+ <c>*arity</c>. Keys and values follow in the following order:
+ <c>K1, V1, K2, V2, ..., Kn, Vn</c>. This makes a total of
+ <c>arity*2</c> terms. If <c>arity</c> is zero, it's an empty map.
+ A correctly encoded map does not have duplicate keys.</p>
+ </desc>
+ </func>
+ <func>
<name><ret>int</ret><nametext>ei_decode_ei_term(const char* buf, int* index, ei_term* term)</nametext></name>
<fsummary>Decode a term, without prior knowledge of type</fsummary>
<desc>
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index 4eb61015cc..ab6f4179d6 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -30,6 +30,22 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.7.16</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix memcheck warning in gen_challange (Thanks to Olivier
+ Girondel)</p>
+ <p>
+ Own Id: OTP-11608</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.7.15</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h
index 9b83385a46..3f3435977d 100644
--- a/lib/erl_interface/include/ei.h
+++ b/lib/erl_interface/include/ei.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2013. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -39,7 +39,7 @@
#include <stdio.h> /* Need type FILE */
#include <errno.h> /* Need EHOSTUNREACH, ENOMEM, ... */
-#if !defined(__WIN32__) && !defined(VXWORKS) || (defined(VXWORKS) && defined(HAVE_SENS))
+#if !(defined(__WIN32__) || defined(_WIN32)) && !defined(VXWORKS) || (defined(VXWORKS) && defined(HAVE_SENS))
# include <netdb.h>
#endif
@@ -131,6 +131,7 @@
#define ERL_SMALL_BIG_EXT 'n'
#define ERL_LARGE_BIG_EXT 'o'
#define ERL_NEW_FUN_EXT 'p'
+#define ERL_MAP_EXT 't'
#define ERL_FUN_EXT 'u'
#define ERL_NEW_CACHE 'N' /* c nodes don't know these two */
@@ -467,6 +468,8 @@ int ei_encode_list_header(char *buf, int *index, int arity);
int ei_x_encode_list_header(ei_x_buff* x, long n);
#define ei_encode_empty_list(buf,i) ei_encode_list_header(buf,i,0)
int ei_x_encode_empty_list(ei_x_buff* x);
+int ei_encode_map_header(char *buf, int *index, int arity);
+int ei_x_encode_map_header(ei_x_buff* x, long n);
/*
* ei_get_type() returns the type and "size" of the item at
@@ -507,6 +510,7 @@ int ei_decode_term(const char *buf, int *index, void *t); /* ETERM** actually */
int ei_decode_trace(const char *buf, int *index, erlang_trace *p);
int ei_decode_tuple_header(const char *buf, int *index, int *arity);
int ei_decode_list_header(const char *buf, int *index, int *arity);
+int ei_decode_map_header(const char *buf, int *index, int *arity);
/*
* ei_decode_ei_term() returns 1 if term is decoded, 0 if term is OK,
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index c9aa28812c..2e8418d61e 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -1166,7 +1166,11 @@ static unsigned int gen_challenge(void)
uname(&s.name);
s.cpu = clock();
s.pid = getpid();
+#ifndef __ANDROID__
s.hid = gethostid();
+#else
+ s.hid = 0;
+#endif
s.uid = getuid();
s.gid = getgid();
@@ -1336,7 +1340,8 @@ static int send_name_or_challenge(int fd, char *nodename,
| DFLAG_NEW_FUN_TAGS
| DFLAG_NEW_FLOATS
| DFLAG_SMALL_ATOM_TAGS
- | DFLAG_UTF8_ATOMS));
+ | DFLAG_UTF8_ATOMS
+ | DFLAG_MAP_TAG));
if (f_chall)
put32be(s, challenge);
memcpy(s, nodename, strlen(nodename));
diff --git a/lib/erl_interface/src/connect/ei_connect_int.h b/lib/erl_interface/src/connect/ei_connect_int.h
index 42ab9b58d7..8fab47a787 100644
--- a/lib/erl_interface/src/connect/ei_connect_int.h
+++ b/lib/erl_interface/src/connect/ei_connect_int.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -104,6 +104,7 @@ extern int h_errno;
#define DFLAG_NEW_FLOATS 0x800
#define DFLAG_SMALL_ATOM_TAGS 0x4000
#define DFLAG_UTF8_ATOMS 0x10000
+#define DFLAG_MAP_TAG 0x20000
ei_cnode *ei_fd_to_cnode(int fd);
int ei_distversion(int fd);
diff --git a/lib/erl_interface/src/connect/ei_resolve.c b/lib/erl_interface/src/connect/ei_resolve.c
index 74dcba61a7..cffcac801c 100644
--- a/lib/erl_interface/src/connect/ei_resolve.c
+++ b/lib/erl_interface/src/connect/ei_resolve.c
@@ -642,7 +642,7 @@ struct hostent *ei_gethostbyname_r(const char *name,
#ifndef HAVE_GETHOSTBYNAME_R
return my_gethostbyname_r(name,hostp,buffer,buflen,h_errnop);
#else
-#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000) || defined(__DragonFly__))
+#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000) || defined(__DragonFly__) || defined(__ANDROID__))
struct hostent *result;
gethostbyname_r(name, hostp, buffer, buflen, &result, h_errnop);
diff --git a/lib/erl_interface/src/decode/decode_skip.c b/lib/erl_interface/src/decode/decode_skip.c
index 553266471c..2260394da1 100644
--- a/lib/erl_interface/src/decode/decode_skip.c
+++ b/lib/erl_interface/src/decode/decode_skip.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -61,7 +61,13 @@ int ei_skip_term(const char* buf, int* index)
break;
case ERL_SMALL_TUPLE_EXT:
case ERL_LARGE_TUPLE_EXT:
- if (ei_decode_tuple_header(buf, index, &n) < 0) return -1;
+ if (ei_decode_tuple_header(buf, index, &n) < 0) return -1;
+ for (i = 0; i < n; ++i)
+ ei_skip_term(buf, index);
+ break;
+ case ERL_MAP_EXT:
+ if (ei_decode_map_header(buf, index, &n) < 0) return -1;
+ n *= 2;
for (i = 0; i < n; ++i)
ei_skip_term(buf, index);
break;
diff --git a/lib/erl_interface/src/decode/decode_tuple_header.c b/lib/erl_interface/src/decode/decode_tuple_header.c
index c0ba14ea47..698be1b97a 100644
--- a/lib/erl_interface/src/decode/decode_tuple_header.c
+++ b/lib/erl_interface/src/decode/decode_tuple_header.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -45,3 +45,24 @@ int ei_decode_tuple_header(const char *buf, int *index, int *arity)
return 0;
}
+
+int ei_decode_map_header(const char *buf, int *index, int *arity)
+{
+ const char *s = buf + *index;
+ const char *s0 = s;
+ int i;
+
+ switch ((i=get8(s))) {
+ case ERL_MAP_EXT:
+ if (arity) *arity = get32be(s);
+ else s += 4;
+ break;
+
+ default:
+ return -1;
+ }
+
+ *index += s-s0;
+
+ return 0;
+}
diff --git a/lib/erl_interface/src/encode/encode_tuple_header.c b/lib/erl_interface/src/encode/encode_tuple_header.c
index 97a3d1f808..5b11e60447 100644
--- a/lib/erl_interface/src/encode/encode_tuple_header.c
+++ b/lib/erl_interface/src/encode/encode_tuple_header.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -47,3 +47,20 @@ int ei_encode_tuple_header(char *buf, int *index, int arity)
return 0;
}
+int ei_encode_map_header(char *buf, int *index, int arity)
+{
+ char *s = buf + *index;
+ char *s0 = s;
+
+ if (arity < 0) return -1;
+
+ if (!buf) s += 5;
+ else {
+ put8(s,ERL_MAP_EXT);
+ put32be(s,arity);
+ }
+
+ *index += s-s0;
+
+ return 0;
+}
diff --git a/lib/erl_interface/src/misc/ei_decode_term.c b/lib/erl_interface/src/misc/ei_decode_term.c
index ce5ae5b19d..2e7317f781 100644
--- a/lib/erl_interface/src/misc/ei_decode_term.c
+++ b/lib/erl_interface/src/misc/ei_decode_term.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -100,6 +100,7 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
term->size = get16be(s);
return 0;
case ERL_LIST_EXT:
+ case ERL_MAP_EXT:
term->arity = get32be(s);
break;
case ERL_BINARY_EXT:
diff --git a/lib/erl_interface/src/misc/ei_x_encode.c b/lib/erl_interface/src/misc/ei_x_encode.c
index 14d0b56b8f..10542c88a5 100644
--- a/lib/erl_interface/src/misc/ei_x_encode.c
+++ b/lib/erl_interface/src/misc/ei_x_encode.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -206,6 +206,16 @@ int ei_x_encode_tuple_header(ei_x_buff* x, long n)
return ei_encode_tuple_header(x->buff, &x->index, n);
}
+int ei_x_encode_map_header(ei_x_buff* x, long n)
+{
+ int i = x->index;
+ if (ei_encode_map_header(NULL, &i, n) == -1)
+ return -1;
+ if (!x_fix_buff(x, i))
+ return -1;
+ return ei_encode_map_header(x->buff, &x->index, n);
+}
+
int ei_x_encode_atom(ei_x_buff* x, const char* s)
{
return ei_x_encode_atom_len_as(x, s, strlen(s), ERLANG_LATIN1, ERLANG_LATIN1);
diff --git a/lib/erl_interface/test/all_SUITE_data/ei_runner.c b/lib/erl_interface/test/all_SUITE_data/ei_runner.c
index cdf32b48c4..196a77dce5 100644
--- a/lib/erl_interface/test/all_SUITE_data/ei_runner.c
+++ b/lib/erl_interface/test/all_SUITE_data/ei_runner.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -182,6 +182,10 @@ char *read_packet(int *len)
return io_buf;
}
+void free_packet(char* packet)
+{
+ free(packet);
+}
/***********************************************************************
* S e n d i n g r e p l i e s
diff --git a/lib/erl_interface/test/all_SUITE_data/ei_runner.h b/lib/erl_interface/test/all_SUITE_data/ei_runner.h
index 96d6a1cbf7..a037341d57 100644
--- a/lib/erl_interface/test/all_SUITE_data/ei_runner.h
+++ b/lib/erl_interface/test/all_SUITE_data/ei_runner.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -44,6 +44,7 @@ void run_tests(char* argv0, TestCase cases[], unsigned number);
int get_bin_term(ei_x_buff* x, ei_term* term);
char *read_packet(int *len);
+void free_packet(char*);
/*
* Sending replies.
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE.erl b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
index c7830f58f2..7caec6ac04 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE.erl
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -127,6 +127,15 @@ test_ei_decode_encode(Config) when is_list(Config) ->
send_rec(P, mk_ref({Atom,1}, [262143, 8723648, 24097245])),
void
end || Atom <- unicode_atom_data()],
+
+ send_rec(P, {}),
+ send_rec(P, {atom, Pid, Port, Ref}),
+ send_rec(P, [atom, Pid, Port, Ref]),
+ send_rec(P, [atom | Fun]),
+ send_rec(P, #{}),
+ send_rec(P, #{key => value}),
+ send_rec(P, maps:put(Port, Ref, #{key => value, key2 => Pid})),
+
?line runner:recv_eot(P),
ok.
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
index 317e5edecd..fcf546105b 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2004-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -32,9 +32,33 @@
/*#define MESSAGE(FMT,A1,A2) message(FMT,A1,A2)*/
#define MESSAGE(FMT,A1,A2)
-typedef int decodeFT(const char *buf, int *index, void*);
-typedef int encodeFT(char *buf, int *index, void*);
-typedef int x_encodeFT(ei_x_buff*, void*);
+
+typedef struct
+{
+ char name[MAXATOMLEN_UTF8];
+ erlang_char_encoding enc;
+}my_atom;
+
+struct my_obj {
+ union {
+ erlang_fun fun;
+ erlang_pid pid;
+ erlang_port port;
+ erlang_ref ref;
+ erlang_trace trace;
+ erlang_big big;
+ my_atom atom;
+
+ int arity;
+ }u;
+
+ int nterms; /* 0 for non-containers */
+ char* startp; /* container start position in decode buffer */
+};
+
+typedef int decodeFT(const char *buf, int *index, struct my_obj*);
+typedef int encodeFT(char *buf, int *index, struct my_obj*);
+typedef int x_encodeFT(ei_x_buff*, struct my_obj*);
struct Type {
char* name;
@@ -44,11 +68,36 @@ struct Type {
x_encodeFT* ei_x_encode_fp;
};
-typedef struct
-{
- char name[MAXATOMLEN_UTF8];
- erlang_char_encoding enc;
-}my_atom;
+
+struct Type fun_type = {
+ "fun", "erlang_fun", (decodeFT*)ei_decode_fun,
+ (encodeFT*)ei_encode_fun, (x_encodeFT*)ei_x_encode_fun
+};
+
+struct Type pid_type = {
+ "pid", "erlang_pid", (decodeFT*)ei_decode_pid,
+ (encodeFT*)ei_encode_pid, (x_encodeFT*)ei_x_encode_pid
+};
+
+struct Type port_type = {
+ "port", "erlang_port", (decodeFT*)ei_decode_port,
+ (encodeFT*)ei_encode_port, (x_encodeFT*)ei_x_encode_port
+};
+
+struct Type ref_type = {
+ "ref", "erlang_ref", (decodeFT*)ei_decode_ref,
+ (encodeFT*)ei_encode_ref, (x_encodeFT*)ei_x_encode_ref
+};
+
+struct Type trace_type = {
+ "trace", "erlang_trace", (decodeFT*)ei_decode_trace,
+ (encodeFT*)ei_encode_trace, (x_encodeFT*)ei_x_encode_trace
+};
+
+struct Type big_type = {
+ "big", "erlang_big", (decodeFT*)ei_decode_big,
+ (encodeFT*)ei_encode_big, (x_encodeFT*)ei_x_encode_big
+};
int ei_decode_my_atom(const char *buf, int *index, my_atom* a)
{
@@ -64,130 +113,274 @@ int ei_x_encode_my_atom(ei_x_buff* x, my_atom* a)
return ei_x_encode_atom_as(x, a->name, ERLANG_UTF8, a->enc);
}
+struct Type my_atom_type = {
+ "atom", "my_atom", (decodeFT*)ei_decode_my_atom,
+ (encodeFT*)ei_encode_my_atom, (x_encodeFT*)ei_x_encode_my_atom
+};
+
+
+int my_decode_tuple_header(const char *buf, int *index, struct my_obj* obj)
+{
+ int ret = ei_decode_tuple_header(buf, index, &obj->u.arity);
+ if (ret == 0 && obj)
+ obj->nterms = obj->u.arity;
+ return ret;
+}
+
+int my_encode_tuple_header(char *buf, int *index, struct my_obj* obj)
+{
+ return ei_encode_tuple_header(buf, index, obj->u.arity);
+}
+int my_x_encode_tuple_header(ei_x_buff* x, struct my_obj* obj)
+{
+ return ei_x_encode_tuple_header(x, (long)obj->u.arity);
+}
+
+struct Type tuple_type = {
+ "tuple_header", "arity", my_decode_tuple_header,
+ my_encode_tuple_header, my_x_encode_tuple_header
+};
+
+
+int my_decode_list_header(const char *buf, int *index, struct my_obj* obj)
+{
+ int ret = ei_decode_list_header(buf, index, &obj->u.arity);
+ if (ret == 0 && obj) {
+ obj->nterms = obj->u.arity + 1;
+ }
+ return ret;
+}
+int my_encode_list_header(char *buf, int *index, struct my_obj* obj)
+{
+ return ei_encode_list_header(buf, index, obj->u.arity);
+}
+int my_x_encode_list_header(ei_x_buff* x, struct my_obj* obj)
+{
+ return ei_x_encode_list_header(x, (long)obj->u.arity);
+}
+
+struct Type list_type = {
+ "list_header", "arity", my_decode_list_header,
+ my_encode_list_header, my_x_encode_list_header
+};
+
+
+int my_decode_nil(const char *buf, int *index, struct my_obj* dummy)
+{
+ int type, size, ret;
+ ret = ei_get_type(buf, index, &type, &size);
+ (*index)++;
+ return ret ? ret : !(type == ERL_NIL_EXT);
+
+}
+int my_encode_nil(char *buf, int *index, struct my_obj* dummy)
+{
+ return ei_encode_empty_list(buf, index);
+}
+
+int my_x_encode_nil(ei_x_buff* x, struct my_obj* dummy)
+{
+ return ei_x_encode_empty_list(x);
+}
+
+struct Type nil_type = {
+ "empty_list", "nil", my_decode_nil,
+ my_encode_nil, my_x_encode_nil
+};
+
+int my_decode_map_header(const char *buf, int *index, struct my_obj* obj)
+{
+ int ret = ei_decode_map_header(buf, index, &obj->u.arity);
+ if (ret == 0 && obj)
+ obj->nterms = obj->u.arity * 2;
+ return ret;
+}
+int my_encode_map_header(char *buf, int *index, struct my_obj* obj)
+{
+ return ei_encode_map_header(buf, index, obj->u.arity);
+}
+int my_x_encode_map_header(ei_x_buff* x, struct my_obj* obj)
+{
+ return ei_x_encode_map_header(x, (long)obj->u.arity);
+}
+
+struct Type map_type = {
+ "map_header", "arity", my_decode_map_header,
+ my_encode_map_header, my_x_encode_map_header
+};
+
+
#define BUFSZ 2000
-void decode_encode(struct Type* t, void* obj)
+void decode_encode(struct Type** tv, int nobj)
{
- char *buf;
- char buf2[BUFSZ];
- int size1 = 0;
- int size2 = 0;
- int size3 = 0;
- int err;
+ struct my_obj objv[10];
+ int oix = 0;
+ char* packet;
+ char* inp;
+ char* outp;
+ char out_buf[BUFSZ];
+ int size1, size2, size3;
+ int err, i;
ei_x_buff arg;
- MESSAGE("ei_decode_%s, arg is type %s", t->name, t->type);
- buf = read_packet(NULL);
- err = t->ei_decode_fp(buf+1, &size1, NULL);
- if (err != 0) {
- if (err != -1) {
- fail("decode returned non zero but not -1");
- } else {
- fail("decode returned non zero");
+ packet = read_packet(NULL);
+ inp = packet+1;
+ outp = out_buf;
+ ei_x_new(&arg);
+ for (i=0; i<nobj; i++) {
+ struct Type* t = tv[i];
+
+ MESSAGE("ei_decode_%s, arg is type %s", t->name, t->type);
+
+ size1 = 0;
+ err = t->ei_decode_fp(inp, &size1, NULL);
+ if (err != 0) {
+ if (err != -1) {
+ fail("decode returned non zero but not -1");
+ } else {
+ fail("decode returned non zero");
+ }
+ return;
+ }
+ if (size1 < 1) {
+ fail("size is < 1");
+ return;
}
- return;
- }
- if (size1 < 1) {
- fail("size is < 1");
- return;
- }
- if (size1 > BUFSZ) {
- fail("size is > BUFSZ");
- return;
- }
+ if (size1 > BUFSZ) {
+ fail("size is > BUFSZ");
+ return;
+ }
- err = t->ei_decode_fp(buf+1, &size2, obj);
- if (err != 0) {
- if (err != -1) {
- fail("decode returned non zero but not -1");
- } else {
- fail("decode returned non zero");
+ size2 = 0;
+ objv[oix].nterms = 0;
+ objv[oix].startp = inp;
+ err = t->ei_decode_fp(inp, &size2, &objv[oix]);
+ if (err != 0) {
+ if (err != -1) {
+ fail("decode returned non zero but not -1");
+ } else {
+ fail("decode returned non zero");
+ }
+ return;
+ }
+ if (size1 != size2) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
+ fail("decode sizes differs");
+ return;
}
- return;
- }
- if (size1 != size2) {
- MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
- fail("decode sizes differs");
- return;
- }
- size2 = 0;
- err = ei_skip_term(buf+1, &size2);
- if (err != 0) {
- fail("ei_skip_term returned non zero");
- return;
- }
- if (size1 != size2) {
- MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
- fail("skip size differs");
- return;
- }
+ if (!objv[oix].nterms) {
+ size2 = 0;
+ err = ei_skip_term(inp, &size2);
+ if (err != 0) {
+ fail("ei_skip_term returned non zero");
+ return;
+ }
+ if (size1 != size2) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
+ fail("skip size differs");
+ return;
+ }
+ }
- MESSAGE("ei_encode_%s buf is NULL, arg is type %s", t->name, t->type);
- size2 = 0;
- err = t->ei_encode_fp(NULL, &size2, obj);
- if (err != 0) {
- if (err != -1) {
- fail("size calculation returned non zero but not -1");
+ MESSAGE("ei_encode_%s buf is NULL, arg is type %s", t->name, t->type);
+ size2 = 0;
+ err = t->ei_encode_fp(NULL, &size2, &objv[oix]);
+ if (err != 0) {
+ if (err != -1) {
+ fail("size calculation returned non zero but not -1");
+ return;
+ } else {
+ fail("size calculation returned non zero");
+ return;
+ }
+ }
+ if (size1 != size2) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
+ fail("decode and encode size differs when buf is NULL");
return;
- } else {
- fail("size calculation returned non zero");
+ }
+ MESSAGE("ei_encode_%s, arg is type %s", t->name, t->type);
+ size3 = 0;
+ err = t->ei_encode_fp(outp, &size3, &objv[oix]);
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1");
+ } else {
+ fail("returned non zero");
+ }
return;
}
- }
- if (size1 != size2) {
- MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
- fail("decode and encode size differs when buf is NULL");
- return;
- }
- MESSAGE("ei_encode_%s, arg is type %s", t->name, t->type);
- err = t->ei_encode_fp(buf2, &size3, obj);
- if (err != 0) {
- if (err != -1) {
- fail("returned non zero but not -1");
- } else {
- fail("returned non zero");
+ if (size1 != size3) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size3);
+ fail("decode and encode size differs");
+ return;
}
- return;
- }
- if (size1 != size3) {
- MESSAGE("size1 = %d, size2 = %d\n",size1,size3);
- fail("decode and encode size differs");
- return;
- }
- send_buffer(buf2, size1);
- MESSAGE("ei_x_encode_%s, arg is type %s", t->name, t->type);
- ei_x_new(&arg);
- err = t->ei_x_encode_fp(&arg, obj);
- if (err != 0) {
- if (err != -1) {
- fail("returned non zero but not -1");
- } else {
- fail("returned non zero");
+ MESSAGE("ei_x_encode_%s, arg is type %s", t->name, t->type);
+ err = t->ei_x_encode_fp(&arg, &objv[oix]);
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1");
+ } else {
+ fail("returned non zero");
+ }
+ ei_x_free(&arg);
+ return;
}
- ei_x_free(&arg);
- return;
+ if (arg.index < 1) {
+ fail("size is < 1");
+ ei_x_free(&arg);
+ return;
+ }
+
+ inp += size1;
+ outp += size1;
+
+ if (objv[oix].nterms) { /* container term */
+ if (++oix >= sizeof(objv)/sizeof(*objv))
+ fail("Term too deep");
+ }
+ else { /* "leaf" term */
+ while (oix > 0) {
+ if (--(objv[oix - 1].nterms) == 0) {
+ /* last element in container */
+ --oix;
+
+ size2 = 0;
+ err = ei_skip_term(objv[oix].startp, &size2);
+ if (err != 0) {
+ fail("ei_skip_term returned non zero");
+ return;
+ }
+ if (objv[oix].startp + size2 != inp) {
+ MESSAGE("size1 = %d, size2 = %d\n", size1, size2);
+ fail("container skip size differs");
+ return;
+ }
+ }
+ else
+ break; /* more elements in container */
+ }
+ }
+
}
- if (arg.index < 1) {
- fail("size is < 1");
- ei_x_free(&arg);
- return;
+ if (oix > 0) {
+ fail("Container not complete");
}
+ send_buffer(out_buf, outp - out_buf);
send_buffer(arg.buff, arg.index);
ei_x_free(&arg);
+ free_packet(packet);
}
+void decode_encode_one(struct Type* t)
+{
+ decode_encode(&t, 1);
+}
-#define EI_DECODE_ENCODE(TYPE, ERLANG_TYPE) { \
- struct Type type_struct = {#TYPE, #ERLANG_TYPE, \
- (decodeFT*)ei_decode_##TYPE, \
- (encodeFT*)ei_encode_##TYPE, \
- (x_encodeFT*)ei_x_encode_##TYPE }; \
- ERLANG_TYPE type_obj; \
- decode_encode(&type_struct, &type_obj); \
- }
void decode_encode_big(struct Type* t)
@@ -274,14 +467,6 @@ void decode_encode_big(struct Type* t)
ei_free_big(p);
}
-#define EI_DECODE_ENCODE_BIG(TYPE, ERLANG_TYPE) { \
- struct Type type_struct = {#TYPE, #ERLANG_TYPE, \
- (decodeFT*)ei_decode_##TYPE, \
- (encodeFT*)ei_encode_##TYPE, \
- (x_encodeFT*)ei_x_encode_##TYPE }; \
- decode_encode_big(&type_struct); \
- }
-
/* ******************************************************************** */
@@ -290,34 +475,63 @@ TESTCASE(test_ei_decode_encode)
{
int i;
- EI_DECODE_ENCODE(fun , erlang_fun);
- EI_DECODE_ENCODE(pid , erlang_pid);
- EI_DECODE_ENCODE(port , erlang_port);
- EI_DECODE_ENCODE(ref , erlang_ref);
- EI_DECODE_ENCODE(trace, erlang_trace);
+ decode_encode_one(&fun_type);
+ decode_encode_one(&pid_type);
+ decode_encode_one(&port_type);
+ decode_encode_one(&ref_type);
+ decode_encode_one(&trace_type);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
+ decode_encode_big(&big_type);
+ decode_encode_big(&big_type);
+ decode_encode_big(&big_type);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
- EI_DECODE_ENCODE_BIG(big , erlang_big);
+ decode_encode_big(&big_type);
+ decode_encode_big(&big_type);
+ decode_encode_big(&big_type);
/* Test large node containers... */
- EI_DECODE_ENCODE(pid , erlang_pid);
- EI_DECODE_ENCODE(port , erlang_port);
- EI_DECODE_ENCODE(ref , erlang_ref);
- EI_DECODE_ENCODE(pid , erlang_pid);
- EI_DECODE_ENCODE(port , erlang_port);
- EI_DECODE_ENCODE(ref , erlang_ref);
+ decode_encode_one(&pid_type);
+ decode_encode_one(&port_type);
+ decode_encode_one(&ref_type);
+ decode_encode_one(&pid_type);
+ decode_encode_one(&port_type);
+ decode_encode_one(&ref_type);
/* Unicode atoms */
for (i=0; i<24; i++) {
- EI_DECODE_ENCODE(my_atom, my_atom);
- EI_DECODE_ENCODE(pid, erlang_pid);
- EI_DECODE_ENCODE(port, erlang_port);
- EI_DECODE_ENCODE(ref, erlang_ref);
+ decode_encode_one(&my_atom_type);
+ decode_encode_one(&pid_type);
+ decode_encode_one(&port_type);
+ decode_encode_one(&ref_type);
+ }
+
+ decode_encode_one(&tuple_type); /* {} */
+ {
+ struct Type* tpl[] = { &tuple_type, &my_atom_type, &pid_type, &port_type, &ref_type };
+ decode_encode(tpl, 5);
+ }
+
+ {
+ struct Type* list[] = { &list_type, &my_atom_type, &pid_type, &port_type, &ref_type, &nil_type };
+ decode_encode(list, 6);
+ }
+ {
+ struct Type* list[] = { &list_type, &my_atom_type, &fun_type };
+ decode_encode(list, 3);
+ }
+ decode_encode_one(&map_type); /* #{} */
+ { /* #{atom => atom}*/
+ struct Type* map[] = { &map_type, &my_atom_type, &my_atom_type };
+ decode_encode(map, 3);
+ }
+
+ { /* #{atom => atom, atom => pid, port => ref }*/
+ struct Type* map[] = { &map_type,
+ &my_atom_type, &my_atom_type,
+ &my_atom_type, &pid_type,
+ &port_type, &ref_type
+ };
+ decode_encode(map, 7);
}
report(1);
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index f386ce09a8..8731283265 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1 +1,2 @@
-EI_VSN = 3.7.15
+EI_VSN = 3.7.16
+ERL_INTERFACE_VSN = $(EI_VSN)
diff --git a/lib/et/doc/src/notes.xml b/lib/et/doc/src/notes.xml
index 815dfada5e..e122187cf3 100644
--- a/lib/et/doc/src/notes.xml
+++ b/lib/et/doc/src/notes.xml
@@ -36,6 +36,51 @@
one section in this document. The title of each section is the
version number of <c>Event Tracer (ET)</c>.</p>
+<section><title>ET 1.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Removed gs based applications and gs based backends. The
+ <c>observer</c> application replaces the removed
+ applications.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10915</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>ET 1.4.4.5</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/et/src/et.app.src b/lib/et/src/et.app.src
index f7189a4197..c26d9320d8 100644
--- a/lib/et/src/et.app.src
+++ b/lib/et/src/et.app.src
@@ -31,5 +31,7 @@
]},
{registered, [et_collector]},
{applications, [stdlib, kernel]},
- {env, []}
+ {env, []},
+ {runtime_dependencies, ["wx-1.2","stdlib-2.0","runtime_tools-1.8.14",
+ "kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/et/vsn.mk b/lib/et/vsn.mk
index 282991aa49..a47be678ca 100644
--- a/lib/et/vsn.mk
+++ b/lib/et/vsn.mk
@@ -1 +1 @@
-ET_VSN = 1.4.4.5
+ET_VSN = 1.5
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index c52f12dcc8..72ffda3cd5 100644
--- a/lib/eunit/doc/src/notes.xml
+++ b/lib/eunit/doc/src/notes.xml
@@ -32,6 +32,35 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.2.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.2.6</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/eunit/src/eunit.app.src b/lib/eunit/src/eunit.app.src
index 5e16dfa2ce..7a3978e200 100644
--- a/lib/eunit/src/eunit.app.src
+++ b/lib/eunit/src/eunit.app.src
@@ -18,4 +18,5 @@
eunit_tty]},
{registered,[]},
{applications, [kernel,stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index 8f816b3b94..f04c0536fe 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.2.6
+EUNIT_VSN = 2.2.7
diff --git a/lib/gs/doc/src/notes.xml b/lib/gs/doc/src/notes.xml
index f5117cb9ba..e40310d36e 100644
--- a/lib/gs/doc/src/notes.xml
+++ b/lib/gs/doc/src/notes.xml
@@ -30,7 +30,36 @@
</header>
<p>This document describes the changes made to the GS application.</p>
- <section><title>GS 1.5.15.2</title>
+ <section><title>GS 1.5.16</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>GS 1.5.15.2</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/gs/src/gs.app.src b/lib/gs/src/gs.app.src
index c83c9b54d7..c6f88e5144 100644
--- a/lib/gs/src/gs.app.src
+++ b/lib/gs/src/gs.app.src
@@ -10,4 +10,5 @@
gstk_window,tcl2erl,tool_file_dialog,tool_utils,
gs_packer,gse]},
{registered, [gs_frontend]},
- {applications, [kernel, stdlib]}]}.
+ {applications, [kernel, stdlib]},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/gs/vsn.mk b/lib/gs/vsn.mk
index 5c18153c34..96786b300c 100644
--- a/lib/gs/vsn.mk
+++ b/lib/gs/vsn.mk
@@ -1,2 +1,2 @@
-GS_VSN = 1.5.15.2
+GS_VSN = 1.5.16
diff --git a/lib/hipe/Makefile b/lib/hipe/Makefile
index a9e24f4d17..46cbc33ae2 100644
--- a/lib/hipe/Makefile
+++ b/lib/hipe/Makefile
@@ -22,7 +22,7 @@ include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
ifdef HIPE_ENABLED
-HIPE_SUBDIRS = regalloc sparc ppc x86 amd64 arm opt tools
+HIPE_SUBDIRS = regalloc sparc ppc x86 amd64 arm opt tools llvm
else
HIPE_SUBDIRS =
endif
diff --git a/lib/hipe/arm/hipe_arm_assemble.erl b/lib/hipe/arm/hipe_arm_assemble.erl
index 2af786994e..e9de96a927 100644
--- a/lib/hipe/arm/hipe_arm_assemble.erl
+++ b/lib/hipe/arm/hipe_arm_assemble.erl
@@ -44,8 +44,8 @@ assemble(CompiledCode, Closures, Exports, Options) ->
print("Total num bytes=~w\n", [CodeSize], Options),
%%
SC = hipe_pack_constants:slim_constmap(ConstMap),
- DataRelocs = mk_data_relocs(RefsFromConsts, LabelMap),
- SSE = slim_sorted_exportmap(ExportMap,Closures,Exports),
+ DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
+ SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap,Closures,Exports),
SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
Bin = term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC},
ConstAlign, ConstSize,
@@ -320,7 +320,7 @@ do_pseudo_li(I, MFA, ConstMap, Address, PrevImms, PendImms) ->
Atom when is_atom(Atom) ->
{load_atom, Atom};
{Label,constant} ->
- ConstNo = find_const({MFA,Label}, ConstMap),
+ ConstNo = hipe_pack_constants:find_const({MFA,Label}, ConstMap),
{load_address, {constant,ConstNo}};
{Label,closure} ->
{load_address, {closure,Label}};
@@ -518,37 +518,6 @@ fix_pc_refs(I, InsnAddress, FunAddress, LabelMap) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-mk_data_relocs(RefsFromConsts, LabelMap) ->
- lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])).
-
-mk_data_relocs([{MFA,Labels} | Rest], LabelMap, Acc) ->
- Map = [case Label of
- {L,Pos} ->
- Offset = find({MFA,L}, LabelMap),
- {Pos,Offset};
- {sorted,Base,OrderedLabels} ->
- {sorted, Base, [begin
- Offset = find({MFA,L}, LabelMap),
- {Order, Offset}
- end
- || {L,Order} <- OrderedLabels]}
- end
- || Label <- Labels],
- %% msg("Map: ~w Map\n",[Map]),
- mk_data_relocs(Rest, LabelMap, [Map,Acc]);
-mk_data_relocs([],_,Acc) -> Acc.
-
-find({_MFA,_L} = MFAL, LabelMap) ->
- gb_trees:get(MFAL, LabelMap).
-
-slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) ->
- IsClosure = lists:member({M,F,A}, Closures),
- IsExported = is_exported(F, A, Exports),
- [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)];
-slim_sorted_exportmap([],_,_) -> [].
-
-is_exported(F, A, Exports) -> lists:member({F,A}, Exports).
-
%%%
%%% Assembly listing support (pp_asm option).
%%%
@@ -594,17 +563,6 @@ fill_spaces(N) when N > 0 ->
fill_spaces(0) ->
[].
-%%%
-%%% Lookup a constant in a ConstMap.
-%%%
-
-find_const({MFA,Label},[{pcm_entry,MFA,Label,ConstNo,_,_,_}|_]) ->
- ConstNo;
-find_const(N,[_|R]) ->
- find_const(N,R);
-find_const(C,[]) ->
- ?EXIT({constant_not_found,C}).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%
diff --git a/lib/hipe/cerl/Makefile b/lib/hipe/cerl/Makefile
index 506e993ff4..d13dfb33c2 100644
--- a/lib/hipe/cerl/Makefile
+++ b/lib/hipe/cerl/Makefile
@@ -42,8 +42,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN)
# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
-MODULES = cerl_cconv cerl_closurean cerl_hipeify \
- cerl_lib cerl_messagean cerl_pmatch cerl_prettypr cerl_to_icode \
+MODULES = cerl_cconv cerl_closurean cerl_hipeify cerl_lib \
+ cerl_messagean cerl_pmatch cerl_prettypr cerl_to_icode \
cerl_typean erl_bif_types erl_types
HRL_FILES= cerl_hipe_primops.hrl
@@ -65,7 +65,7 @@ DOC_FILES= $(MODULES:%=$(DOCS)/%.html)
include ../native.mk
-ERL_COMPILE_FLAGS += +inline +warn_exported_vars +warn_unused_import +warn_missing_spec# +warn_untyped_record
+ERL_COMPILE_FLAGS += -Werror +inline +warn_exported_vars +warn_unused_import +warn_missing_spec #+warn_untyped_record
# ----------------------------------------------------
# Targets
@@ -107,7 +107,6 @@ release_spec: opt
release_docs_spec:
-$(EBIN)/cerl_to_icode.beam: cerl_hipe_primops.hrl ../icode/hipe_icode_primops.hrl
+$(EBIN)/cerl_cconv.beam: cerl_hipe_primops.hrl
$(EBIN)/cerl_hipeify.beam: cerl_hipe_primops.hrl
-$(EBIN)/cerl_lambdalift.beam: cerl_hipe_primops.hrl
-$(EBIN)/erl_bif_types.beam: ../icode/hipe_icode_primops.hrl
+$(EBIN)/cerl_to_icode.beam: cerl_hipe_primops.hrl ../icode/hipe_icode_primops.hrl
diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl
index 22f5b8945a..9a3873f46d 100644
--- a/lib/hipe/cerl/cerl_prettypr.erl
+++ b/lib/hipe/cerl/cerl_prettypr.erl
@@ -63,7 +63,8 @@
seq_arg/1, seq_body/1, string_lit/1, try_arg/1,
try_body/1, try_vars/1, try_evars/1, try_handler/1,
tuple_es/1, type/1, values_es/1, var_name/1,
- map_es/1, map_pair_key/1, map_pair_val/1, map_pair_op/1
+ c_map/1, map_arg/1, map_es/1, is_c_map_empty/1,
+ c_map_pair/2, map_pair_key/1, map_pair_val/1, map_pair_op/1
]).
-define(PAPER, 76).
@@ -489,7 +490,13 @@ lay_literal(Node, Ctxt) ->
%% `lay_cons' will check for strings.
lay_cons(Node, Ctxt);
V when is_tuple(V) ->
- lay_tuple(Node, Ctxt)
+ lay_tuple(Node, Ctxt);
+ M when is_map(M), map_size(M) =:= 0 ->
+ text("~{}~");
+ M when is_map(M) ->
+ lay_map(c_map([c_map_pair(abstract(K),abstract(V))
+ || {K,V} <- maps:to_list(M)]),
+ Ctxt)
end.
lay_var(Node, Ctxt) ->
@@ -596,10 +603,17 @@ lay_tuple(Node, Ctxt) ->
floating(text("}")))).
lay_map(Node, Ctxt) ->
+ Arg = map_arg(Node),
+ After = case is_c_map_empty(Arg) of
+ true -> floating(text("}~"));
+ false ->
+ beside(floating(text(" | ")),
+ beside(lay(Arg,Ctxt),
+ floating(text("}~"))))
+ end,
beside(floating(text("~{")),
- beside(par(seq(map_es(Node), floating(text(",")),
- Ctxt, fun lay/2)),
- floating(text("}~")))).
+ beside(par(seq(map_es(Node), floating(text(",")), Ctxt, fun lay/2)),
+ After)).
lay_map_pair(Node, Ctxt) ->
K = map_pair_key(Node),
diff --git a/lib/hipe/cerl/cerl_to_icode.erl b/lib/hipe/cerl/cerl_to_icode.erl
index 1c1c10d9b0..2645056be1 100644
--- a/lib/hipe/cerl/cerl_to_icode.erl
+++ b/lib/hipe/cerl/cerl_to_icode.erl
@@ -29,9 +29,9 @@
-define(NO_UNUSED, true).
--export([module/2]).
+-export([module/1, module/2]).
-ifndef(NO_UNUSED).
--export([function/3, function/4, module/1]).
+-export([function/3, function/4]).
-endif.
%% Added in an attempt to suppress message by Dialyzer, but I run into
@@ -102,36 +102,32 @@
%% Record definitions
--record(ctxt, {final = false :: boolean(),
- effect = false,
- fail = [], % [] or fail-to label
- class = expr, % expr | guard
- line = 0, % current line number
- 'receive' % undefined | #receive{}
- }).
-
-record('receive', {loop}).
-record(cerl_to_icode__var, {name}).
-record('fun', {label, vars}).
+-record(ctxt, {final = false :: boolean(),
+ effect = false :: boolean(),
+ fail = [], % [] or fail-to label
+ class = expr :: 'expr' | 'guard',
+ line = 0 :: erl_scan:line(), % current line number
+ 'receive' :: 'undefined' | #'receive'{}
+ }).
%% ---------------------------------------------------------------------
%% Code
-
-%% @spec module(Module::cerl()) -> [icode()]
+%% @spec module(Module::cerl()) -> [{mfa(), icode()}]
%% @equiv module(Module, [])
--ifndef(NO_UNUSED).
+-spec module(cerl:c_module()) -> [{mfa(), hipe_icode:icode()}].
+
module(E) ->
module(E, []).
--endif.
-%% @clear
-
-%% @spec module(Module::cerl(), Options::[term()]) -> [icode()]
+%% @spec module(Module::cerl(), Options::[term()]) -> [{mfa(), icode()}]
%%
-%% cerl() = cerl:cerl()
+%% cerl() = cerl:c_module()
%% icode() = hipe_icode:icode()
%%
%% @doc Transforms a Core Erlang module to linear HiPE Icode. The result
@@ -149,7 +145,7 @@ module(E) ->
%% @see function/4
%% @see cerl_hipeify:transform/1
-%% -spec module(cerl:c_module(), [term()]) -> [{mfa(), hipe_icode:icode()}].
+-spec module(cerl:c_module(), [term()]) -> [{mfa(), hipe_icode:icode()}].
module(E, Options) ->
module_1(cerl_hipeify:transform(E, Options), Options).
@@ -163,8 +159,8 @@ module_1(E, Options) ->
throw(error)
end,
S0 = init(M),
- S1 = s__set_pmatch(proplists:get_value(pmatch, Options), S0),
- S2 = s__set_bitlevel_binaries(proplists:get_value(
+ S1 = s__set_pmatch(proplists:get_value(pmatch, Options), S0),
+ S2 = s__set_bitlevel_binaries(proplists:get_value(
bitlevel_binaries, Options), S1),
{Icode, _} = lists:mapfoldl(fun function_definition/2,
S2, cerl:module_defs(E)),
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 0512d4bc3c..a460f16272 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -1073,14 +1073,14 @@ type(hipe_bifs, fun_to_address, 1, Xs, Opaques) ->
%% type(hipe_bifs, get_emu_address, 1, Xs, Opaques) ->
%% strict(hipe_bifs, get_emu_address, 1, Xs,
%% fun (_) -> t_integer() end, Opaques); % address
+type(hipe_bifs, get_fe, 2, Xs, Opaques) ->
+ strict(hipe_bifs, get_fe, 2, Xs, fun (_) -> t_integer() end, Opaques);
type(hipe_bifs, get_rts_param, 1, Xs, Opaques) ->
strict(hipe_bifs, get_rts_param, 1, Xs,
fun (_) -> t_sup(t_integer(), t_nil()) end, Opaques);
type(hipe_bifs, invalidate_funinfo_native_addresses, 1, Xs, Opaques) ->
strict(hipe_bifs, invalidate_funinfo_native_addresses, 1, Xs,
fun (_) -> t_nil() end, Opaques);
-type(hipe_bifs, make_fe, 3, Xs, Opaques) ->
- strict(hipe_bifs, make_fe, 3, Xs, fun (_) -> t_integer() end, Opaques);
%% type(hipe_bifs, make_native_stub, 2, Xs, Opaques) ->
%% strict(hipe_bifs, make_native_stub, 2, Xs,
%% fun (_) -> t_integer() end, Opaques); % address
@@ -1116,6 +1116,9 @@ type(hipe_bifs, set_funinfo_native_address, 3, Xs, Opaques) ->
type(hipe_bifs, set_native_address, 3, Xs, Opaques) ->
strict(hipe_bifs, set_native_address, 3, Xs,
fun (_) -> t_nil() end, Opaques);
+type(hipe_bifs, set_native_address_in_fe, 2, Xs, Opaques) ->
+ strict(hipe_bifs, set_native_address_in_fe, 2, Xs,
+ fun (_) -> t_atom('true') end, Opaques);
type(hipe_bifs, system_crc, 1, Xs, Opaques) ->
strict(hipe_bifs, system_crc, 1, Xs, fun (_) -> t_crc32() end, Opaques);
type(hipe_bifs, term_to_word, 1, Xs, Opaques) ->
@@ -2451,12 +2454,12 @@ arg_types(hipe_bifs, fun_to_address, 1) ->
[t_mfa()];
%% arg_types(hipe_bifs, get_emu_address, 1) ->
%% [t_mfa()];
+arg_types(hipe_bifs, get_fe, 2) ->
+ [t_atom(), t_tuple([t_integer(), t_integer(), t_integer()])];
arg_types(hipe_bifs, get_rts_param, 1) ->
[t_fixnum()];
arg_types(hipe_bifs, invalidate_funinfo_native_addresses, 1) ->
[t_list(t_mfa())];
-arg_types(hipe_bifs, make_fe, 3) ->
- [t_integer(), t_atom(), t_tuple([t_integer(), t_integer(), t_integer()])];
%% arg_types(hipe_bifs, make_native_stub, 2) ->
%% [t_integer(), t_arity()];
arg_types(hipe_bifs, mark_referred_from, 1) ->
@@ -2485,6 +2488,8 @@ arg_types(hipe_bifs, set_funinfo_native_address, 3) ->
arg_types(hipe_bifs, set_native_address, 3);
arg_types(hipe_bifs, set_native_address, 3) ->
[t_mfa(), t_integer(), t_boolean()];
+arg_types(hipe_bifs, set_native_address_in_fe, 2) ->
+ [t_integer(), t_integer()];
arg_types(hipe_bifs, system_crc, 1) ->
[t_crc32()];
arg_types(hipe_bifs, term_to_word, 1) ->
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 32390045e3..67661130a5 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -126,6 +126,8 @@
t_is_instance/2,
t_is_integer/1, t_is_integer/2,
t_is_list/1,
+ t_is_map/1,
+ t_is_map/2,
t_is_matchstate/1,
t_is_nil/1, t_is_nil/2,
t_is_non_neg_integer/1,
@@ -148,6 +150,8 @@
t_list/1,
t_list_elements/1, t_list_elements/2,
t_list_termination/1,
+ t_map/0,
+ t_map/1,
t_matchstate/0,
t_matchstate/2,
t_matchstate_present/1,
@@ -205,14 +209,11 @@
type_is_defined/4,
record_field_diffs_to_string/2,
subst_all_vars_to_any/1,
+ subst_all_remote/2,
lift_list_to_pos_empty/1,
is_opaque_type/2,
is_erl_type/1,
- atom_to_string/1,
-
- t_is_map/2,
- t_map/1,
- t_map/0
+ atom_to_string/1
]).
%%-define(DO_ERL_TYPES_TEST, true).
@@ -498,9 +499,9 @@ t_contains_opaque(?int_range(_From, _To), _Opaques) -> false;
t_contains_opaque(?int_set(_Set), _Opaques) -> false;
t_contains_opaque(?list(Type, Tail, _), Opaques) ->
t_contains_opaque(Type, Opaques) orelse t_contains_opaque(Tail, Opaques);
-t_contains_opaque(?map(Pairs), Opaques) ->
- list_contains_opaque([V||{_,V}<-Pairs], Opaques) orelse
- list_contains_opaque([K||{K,_}<-Pairs], Opaques);
+t_contains_opaque(?map(_) = Map, Opaques) ->
+ list_contains_opaque(map_values(Map), Opaques) orelse
+ list_contains_opaque(map_keys(Map), Opaques);
t_contains_opaque(?matchstate(_P, _Slots), _Opaques) -> false;
t_contains_opaque(?nil, _Opaques) -> false;
t_contains_opaque(?number(_Set, _Tag), _Opaques) -> false;
@@ -614,7 +615,7 @@ t_decorate_with_opaque(T1, T2, Opaques) ->
end
end.
-decorate(?none=Type, _, _Opaques) -> Type;
+decorate(Type, ?none, _Opaques) -> Type;
decorate(?function(Domain, Range), ?function(D, R), Opaques) ->
?function(decorate(Domain, D, Opaques), decorate(Range, R, Opaques));
decorate(?list(Types, Tail, Size), ?list(Ts, Tl, _Sz), Opaques) ->
@@ -680,6 +681,7 @@ union_decorate(U1, U2, Opaques) ->
List = [A,B,F,I,L,N,T,M,Map],
DecList = [Dec ||
E <- List,
+ not t_is_none(E),
not t_is_none(Dec = decorate(E, Opaque, Opaques))],
t_sup([Union|DecList]).
@@ -1728,11 +1730,16 @@ lift_list_to_pos_empty(?list(Content, Termination, _)) ->
t_map() ->
?map([]).
--spec t_map([{erl_type(),erl_type()}]) -> erl_type().
+-spec t_map([{erl_type(), erl_type()}]) -> erl_type().
t_map(_) ->
?map([]).
+-spec t_is_map(erl_type()) -> boolean().
+
+t_is_map(Type) ->
+ t_is_map(Type, 'universe').
+
-spec t_is_map(erl_type(), opaques()) -> boolean().
t_is_map(Type, Opaques) ->
@@ -1741,7 +1748,6 @@ t_is_map(Type, Opaques) ->
is_map1(?map(_)) -> true;
is_map1(_) -> false.
-
%%-----------------------------------------------------------------------------
%% Tuples
%%
@@ -2089,6 +2095,8 @@ t_has_var(?tuple(Elements, _, _)) ->
t_has_var_list(Elements);
t_has_var(?tuple_set(_) = T) ->
t_has_var_list(t_tuple_subtypes(T));
+t_has_var(?map(_)= Map) ->
+ t_has_var_list(map_keys(Map)) orelse t_has_var_list(map_values(Map));
t_has_var(?opaque(Set)) ->
%% Assume variables in 'args' are also present i 'struct'
t_has_var_list([O#opaque.struct || O <- set_to_list(Set)]);
@@ -2116,21 +2124,28 @@ t_collect_vars(?function(Domain, Range), Acc) ->
t_collect_vars(?list(Contents, Termination, _), Acc) ->
ordsets:union(t_collect_vars(Contents, Acc), t_collect_vars(Termination, []));
t_collect_vars(?product(Types), Acc) ->
- lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc, Types);
+ t_collect_vars_list(Types, Acc);
t_collect_vars(?tuple(?any, ?any, ?any), Acc) ->
Acc;
t_collect_vars(?tuple(Types, _, _), Acc) ->
- lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc, Types);
+ t_collect_vars_list(Types, Acc);
t_collect_vars(?tuple_set(_) = TS, Acc) ->
- lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc,
- t_tuple_subtypes(TS));
+ t_collect_vars_list(t_tuple_subtypes(TS), Acc);
+t_collect_vars(?map(_) = Map, Acc0) ->
+ Acc = t_collect_vars_list(map_keys(Map), Acc0),
+ t_collect_vars_list(map_values(Map), Acc);
t_collect_vars(?opaque(Set), Acc) ->
%% Assume variables in 'args' are also present i 'struct'
- lists:foldl(fun(T, TmpAcc) -> t_collect_vars(T, TmpAcc) end, Acc,
- [O#opaque.struct || O <- set_to_list(Set)]);
+ 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.
%%=============================================================================
%%
@@ -2153,6 +2168,7 @@ t_from_term(T) when is_function(T) ->
{arity, Arity} = erlang:fun_info(T, arity),
t_fun(Arity, t_any());
t_from_term(T) when is_integer(T) -> t_integer(T);
+t_from_term(T) when is_map(T) -> t_map();
t_from_term(T) when is_pid(T) -> t_pid();
t_from_term(T) when is_port(T) -> t_port();
t_from_term(T) when is_reference(T) -> t_reference();
@@ -2555,7 +2571,7 @@ force_union(T = ?function(_, _)) -> ?function_union(T);
force_union(T = ?identifier(_)) -> ?identifier_union(T);
force_union(T = ?list(_, _, _)) -> ?list_union(T);
force_union(T = ?nil) -> ?list_union(T);
-force_union(T = ?number(_,_)) -> ?number_union(T);
+force_union(T = ?number(_, _)) -> ?number_union(T);
force_union(T = ?opaque(_)) -> ?opaque_union(T);
force_union(T = ?remote(_)) -> ?remote_union(T);
force_union(T = ?map(_)) -> ?map_union(T);
@@ -2970,16 +2986,19 @@ inf_union(U1, U2, Opaques) ->
List = [A,B,F,I,L,N,T,M,Map],
inf_union_collect(List, Opaque, InfFun, [], [])
end,
- O1 = OpaqueFun(U1, U2, fun(E, Opaque) -> t_inf(Opaque, E, Opaques) end),
- O2 = OpaqueFun(U2, U1, fun(E, Opaque) -> t_inf(E, Opaque, Opaques) end),
- Union = inf_union(U1, U2, 0, [], Opaques),
- t_sup([O1, O2, Union]).
+ {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(hd(ThrowList));
+ Sup -> Sup
+ end.
inf_union_collect([], _Opaque, _InfFun, InfList, ThrowList) ->
- case t_sup(InfList) of
- ?none when ThrowList =/= [] -> throw(hd(lists:flatten(ThrowList)));
- Sup -> Sup
- end;
+ {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) ->
@@ -2990,19 +3009,21 @@ inf_union_collect([E|L], Opaque, InfFun, InfList, ThrowList) ->
inf_union_collect(L, Opaque, InfFun, InfList, [N|ThrowList])
end.
-inf_union([?none|Left1], [?none|Left2], N, Acc, Opaques) ->
- inf_union(Left1, Left2, N, [?none|Acc], Opaques);
-inf_union([T1|Left1], [T2|Left2], N, Acc, Opaques) ->
- case t_inf(T1, T2, Opaques) of
- ?none -> inf_union(Left1, Left2, N, [?none|Acc], Opaques);
- T -> inf_union(Left1, Left2, N+1, [T|Acc], Opaques)
+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:N when is_integer(N) ->
+ inf_union(Left1, Left2, N, [?none|Acc], [N|ThrowList], Opaques)
end;
-inf_union([], [], N, Acc, _Opaques) ->
- if N =:= 0 -> ?none;
+inf_union([], [], N, Acc, ThrowList, _Opaques) ->
+ if N =:= 0 -> {?none, ThrowList};
N =:= 1 ->
[Type] = [T || T <- Acc, T =/= ?none],
- Type;
- N >= 2 -> ?union(lists:reverse(Acc))
+ {Type, ThrowList};
+ N >= 2 -> {?union(lists:reverse(Acc)), ThrowList}
end.
inf_bitstr(U1, B1, U2, B2) ->
@@ -3076,6 +3097,9 @@ t_subst_dict(?tuple(Elements, _Arity, _Tag), Dict) ->
t_tuple([t_subst_dict(E, Dict) || E <- Elements]);
t_subst_dict(?tuple_set(_) = TS, Dict) ->
t_sup([t_subst_dict(T, Dict) || T <- t_tuple_subtypes(TS)]);
+t_subst_dict(?map(Pairs), Dict) ->
+ ?map([{t_subst_dict(K, Dict), t_subst_dict(V, Dict)} ||
+ {K, V} <- Pairs]);
t_subst_dict(?opaque(Es), Dict) ->
List = [Opaque#opaque{args = [t_subst_dict(Arg, Dict) || Arg <- Args],
struct = t_subst_dict(S, Dict)} ||
@@ -3125,6 +3149,9 @@ t_subst_aux(?tuple(Elements, _Arity, _Tag), VarMap) ->
t_tuple([t_subst_aux(E, VarMap) || E <- Elements]);
t_subst_aux(?tuple_set(_) = TS, VarMap) ->
t_sup([t_subst_aux(T, VarMap) || T <- t_tuple_subtypes(TS)]);
+t_subst_aux(?map(Pairs), VarMap) ->
+ ?map([{t_subst_aux(K, VarMap), t_subst_aux(V, VarMap)} ||
+ {K, V} <- Pairs]);
t_subst_aux(?opaque(Es), VarMap) ->
List = [Opaque#opaque{args = [t_subst_aux(Arg, VarMap) || Arg <- Args],
struct = t_subst_aux(S, VarMap)} ||
@@ -3135,6 +3162,18 @@ t_subst_aux(?union(List), VarMap) ->
t_subst_aux(T, _VarMap) ->
T.
+-spec subst_all_remote(erl_type(), erl_type()) -> erl_type().
+
+subst_all_remote(Type0, Substitute) ->
+ Map =
+ fun(Type) ->
+ case erl_types:t_is_remote(Type) of
+ true -> Substitute;
+ false -> Type
+ end
+ end,
+ erl_types:t_map(Map, Type0).
+
%%-----------------------------------------------------------------------------
%% Unification
%%
@@ -3560,6 +3599,8 @@ t_subtract(?product(Elements1) = T1, ?product(Elements2)) ->
_ -> T1
end
end;
+t_subtract(?map(_) = T, _) -> % XXX: very crude; will probably need refinement
+ T;
t_subtract(?product(P1), _) ->
?product(P1);
t_subtract(T, ?product(_)) ->
@@ -3700,7 +3741,7 @@ t_unopaque(T) ->
t_unopaque(?opaque(_) = T, Opaques) ->
case Opaques =:= 'universe' orelse is_opaque_type(T, Opaques) of
true -> t_unopaque(t_opaque_structure(T), Opaques);
- false -> T % XXX: needs revision for parametric opaque data types
+ false -> T
end;
t_unopaque(?list(ElemT, Termination, Sz), Opaques) ->
?list(t_unopaque(ElemT, Opaques), t_unopaque(Termination, Opaques), Sz);
@@ -3720,11 +3761,12 @@ t_unopaque(?union([A,B,F,I,L,N,T,M,O,R,Map]), Opaques) ->
UL = t_unopaque(L, Opaques),
UT = t_unopaque(T, Opaques),
UF = t_unopaque(F, Opaques),
+ UMap = t_unopaque(Map, Opaques),
{OF,UO} = case t_unopaque(O, Opaques) of
?opaque(_) = O1 -> {O1, []};
Type -> {?none, [Type]}
end,
- t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R,Map])|UO]);
+ t_sup([?union([A,B,UF,I,UL,N,UT,M,OF,R,UMap])|UO]);
t_unopaque(T, _) ->
T.
@@ -4231,8 +4273,8 @@ t_from_form({type, _L, list, []}, _TypeNames, _RecDict, _VarDict) ->
t_from_form({type, _L, list, [Type]}, TypeNames, RecDict, VarDict) ->
{T, R} = t_from_form(Type, TypeNames, RecDict, VarDict),
{t_list(T), R};
-t_from_form({type, _L, map, _}, _TypeNames, _RecDict, _VarDict) ->
- {t_map([]), []};
+t_from_form({type, _L, map, _}, TypeNames, RecDict, VarDict) ->
+ builtin_type(map, t_map([]), TypeNames, RecDict, VarDict);
t_from_form({type, _L, mfa, []}, _TypeNames, _RecDict, _VarDict) ->
{t_mfa(), []};
t_from_form({type, _L, module, []}, _TypeNames, _RecDict, _VarDict) ->
@@ -4445,7 +4487,9 @@ get_mod_record([{FieldName, DeclType}|Left1],
[{FieldName, ModType}|Left2], Acc) ->
ModTypeNoVars = subst_all_vars_to_any(ModType),
case
- t_is_remote(ModTypeNoVars) orelse t_is_subtype(ModTypeNoVars, DeclType)
+ contains_remote(ModTypeNoVars)
+ orelse contains_remote(DeclType)
+ orelse t_is_subtype(ModTypeNoVars, DeclType)
of
false -> {error, FieldName};
true -> get_mod_record(Left1, Left2, [{FieldName, ModType}|Acc])
@@ -4459,6 +4503,10 @@ get_mod_record(DeclFields, [], Acc) ->
get_mod_record(_, [{FieldName2, _ModType}|_], _Acc) ->
{error, FieldName2}.
+contains_remote(Type) ->
+ TypeNoRemote = subst_all_remote(Type, t_none()),
+ not t_is_equal(Type, TypeNoRemote).
+
fields_from_form([], _TypeNames, _RecDict, _VarDict) ->
{[], []};
fields_from_form([{Name, Type}|Tail], TypeNames, RecDict,
@@ -4695,6 +4743,12 @@ is_same_type_name2(gb_trees, gb_tree, [], gb_trees, tree, [_, _]) -> true;
is_same_type_name2(gb_trees, tree, [_, _], gb_trees, gb_tree, []) -> true;
is_same_type_name2(_, _, _, _, _, _) -> false.
+map_keys(?map(Pairs)) ->
+ [K || {K, _} <- Pairs].
+
+map_values(?map(Pairs)) ->
+ [V || {_, V} <- Pairs].
+
%% -----------------------------------
%% Set
%%
diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml
index e0b1622d19..c7faf733c7 100644
--- a/lib/hipe/doc/src/notes.xml
+++ b/lib/hipe/doc/src/notes.xml
@@ -30,6 +30,124 @@
</header>
<p>This document describes the changes made to HiPE.</p>
+<section><title>Hipe 3.10.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix compilation with 'no_remove_comments' (Thanks to
+ Johannes Weißl)</p>
+ <p>
+ Own Id: OTP-11564</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ <item>
+ <p>
+ There is now a test suite for the Hipe application</p>
+ <p>
+ Own Id: OTP-11748</p>
+ </item>
+ <item>
+ <p>
+ Support for a LLVM backend has been added in HiPE</p>
+ <p>
+ Own Id: OTP-11801</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The pre-defined types <c>array/0</c>, <c>dict/0</c>,
+ <c>digraph/0</c>, <c>gb_set/0</c>, <c>gb_tree/0</c>,
+ <c>queue/0</c>, <c>set/0</c>, and <c>tid/0</c> have been
+ deprecated. They will be removed in Erlang/OTP 18.0. </p>
+ <p> Instead the types <c>array:array/0</c>,
+ <c>dict:dict/0</c>, <c>digraph:graph/0</c>,
+ <c>gb_set:set/0</c>, <c>gb_tree:tree/0</c>,
+ <c>queue:queue/0</c>, <c>sets:set/0</c>, and
+ <c>ets:tid/0</c> can be used. (Note: it has always been
+ necessary to use <c>ets:tid/0</c>.) </p> <p> It is
+ allowed in Erlang/OTP 17.0 to locally re-define the types
+ <c>array/0</c>, <c>dict/0</c>, and so on. </p> <p> New
+ types <c>array:array/1</c>, <c>dict:dict/2</c>,
+ <c>gb_sets:set/1</c>, <c>gb_trees:tree/2</c>,
+ <c>queue:queue/1</c>, and <c>sets:set/1</c> have been
+ added. </p> <p> A compiler option,
+ <c>nowarn_deprecated_type</c>, has been introduced. By
+ including the attribute </p> <c>
+ -compile(nowarn_deprecated_type).</c> <p> in an Erlang
+ source file, warnings about deprecated types can be
+ avoided in Erlang/OTP 17.0. </p> <p> The option can also
+ be given as a compiler flag: </p> <c> erlc
+ +nowarn_deprecated_type file.erl</c>
+ <p>
+ Own Id: OTP-10342</p>
+ </item>
+ <item>
+ <p>
+ EEP43: New data type - Maps</p>
+ <p>
+ With Maps you may for instance: <taglist> <item><c>M0 =
+ #{ a =&gt; 1, b =&gt; 2}, % create
+ associations</c></item> <item><c>M1 = M0#{ a := 10 }, %
+ update values</c></item> <item><c>M2 = M1#{ "hi" =&gt;
+ "hello"}, % add new associations</c></item> <item><c>#{
+ "hi" := V1, a := V2, b := V3} = M2. % match keys with
+ values</c></item> </taglist></p>
+ <p>
+ For information on how to use Maps please see the
+ <seealso marker="doc/reference_manual:maps">Reference
+ Manual</seealso>.</p>
+ <p>
+ The current implementation is without the following
+ features: <taglist> <item>No variable keys</item>
+ <item>No single value access</item> <item>No map
+ comprehensions</item> </taglist></p>
+ <p>
+ Note that Maps is <em>experimental</em> during OTP 17.0.</p>
+ <p>
+ Own Id: OTP-11616</p>
+ </item>
+ <item>
+ <p> Parameterized opaque types have been introduced. </p>
+ <p>
+ Own Id: OTP-11625</p>
+ </item>
+ <item>
+ <p>
+ Add support for the compilation of the is_map/1 and
+ map_size/1 guards to native code.</p>
+ <p>
+ Own Id: OTP-11831</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Hipe 3.10.2.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl
index 81249c958e..4691662f9f 100644
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ b/lib/hipe/icode/hipe_beam_to_icode.erl
@@ -509,6 +509,10 @@ trans_fun([{test,test_arity,{f,Lbl},[Reg,N]}|Instructions], Env) ->
I = hipe_icode:mk_type([trans_arg(Reg)],{tuple,N},
hipe_icode:label_name(True),map_label(Lbl)),
[I,True | trans_fun(Instructions,Env)];
+%%--- is_map ---
+trans_fun([{test,is_map,{f,Lbl},[Arg]}|Instructions], Env) ->
+ {Code,Env1} = trans_type_test(map,Lbl,Arg,Env),
+ [Code | trans_fun(Instructions,Env1)];
%%--------------------------------------------------------------------
%%--- select_val ---
trans_fun([{select_val,Reg,{f,Lbl},{list,Cases}}|Instructions], Env) ->
@@ -1121,6 +1125,49 @@ trans_fun([{trim,N,NY}|Instructions], Env) ->
trans_fun([{line,_}|Instructions], Env) ->
trans_fun(Instructions,Env);
%%--------------------------------------------------------------------
+%% Map instructions added in Spring 2014 (17.0).
+%%--------------------------------------------------------------------
+trans_fun([{test,has_map_fields,{f,Lbl},Map,{list,Keys}}|Instructions], Env) ->
+ {MapMove, MapVar, Env1} = mk_move_and_var(Map, Env),
+ %% We assume that hipe_icode:mk_call has no side-effects, and reuse
+ %% the help function of get_map_elements below, discarding the value
+ %% assignment instruction list.
+ {TestInstructions, _GetInstructions, Env2} =
+ trans_map_query(MapVar, map_label(Lbl), Env1,
+ lists:flatten([[K, {r, 0}] || K <- Keys])),
+ [MapMove, TestInstructions | trans_fun(Instructions, Env2)];
+trans_fun([{get_map_elements,{f,Lbl},Map,{list,KVPs}}|Instructions], Env) ->
+ {MapMove, MapVar, Env1} = mk_move_and_var(Map, Env),
+ {TestInstructions, GetInstructions, Env2} =
+ trans_map_query(MapVar, map_label(Lbl), Env1, KVPs),
+ [MapMove, TestInstructions, GetInstructions | trans_fun(Instructions, Env2)];
+%%--- put_map_assoc ---
+trans_fun([{put_map_assoc,{f,Lbl},Map,Dst,_N,{list,Pairs}}|Instructions], Env) ->
+ {MapMove, MapVar, Env1} = mk_move_and_var(Map, Env),
+ TempMapVar = mk_var(new),
+ TempMapMove = hipe_icode:mk_move(TempMapVar, MapVar),
+ {PutInstructions, Env2}
+ = case Lbl > 0 of
+ true ->
+ gen_put_map_instrs(exists, assoc, TempMapVar, Dst, Lbl, Pairs, Env1);
+ false ->
+ gen_put_map_instrs(new, assoc, TempMapVar, Dst, new, Pairs, Env1)
+ end,
+ [MapMove, TempMapMove, PutInstructions | trans_fun(Instructions, Env2)];
+%%--- put_map_exact ---
+trans_fun([{put_map_exact,{f,Lbl},Map,Dst,_N,{list,Pairs}}|Instructions], Env) ->
+ {MapMove, MapVar, Env1} = mk_move_and_var(Map, Env),
+ TempMapVar = mk_var(new),
+ TempMapMove = hipe_icode:mk_move(TempMapVar, MapVar),
+ {PutInstructions, Env2}
+ = case Lbl > 0 of
+ true ->
+ gen_put_map_instrs(exists, exact, TempMapVar, Dst, Lbl, Pairs, Env1);
+ false ->
+ gen_put_map_instrs(new, exact, TempMapVar, Dst, new, Pairs, Env1)
+ end,
+ [MapMove, TempMapMove, PutInstructions | trans_fun(Instructions, Env2)];
+%%--------------------------------------------------------------------
%%--- ERROR HANDLING ---
%%--------------------------------------------------------------------
trans_fun([X|_], _) ->
@@ -1500,6 +1547,102 @@ trans_type_test2(function2, Lbl, Arg, Arity, Env) ->
hipe_icode:label_name(True), map_label(Lbl)),
{[Move1,Move2,I,True],Env2}.
+%%
+%% Handles the get_map_elements instruction and the has_map_fields
+%% test instruction.
+%%
+trans_map_query(_MapVar, _FailLabel, Env, []) ->
+ {[], [], Env};
+trans_map_query(MapVar, FailLabel, Env, [Key,Val|KVPs]) ->
+ {Move,KeyVar,Env1} = mk_move_and_var(Key,Env),
+ PassLabel = mk_label(new),
+ BoolVar = hipe_icode:mk_new_var(),
+ ValVar = mk_var(Val),
+ IsKeyCall = hipe_icode:mk_call([BoolVar], maps, is_key, [KeyVar, MapVar],
+ remote),
+ TrueTest = hipe_icode:mk_if('=:=', [BoolVar, hipe_icode:mk_const(true)],
+ hipe_icode:label_name(PassLabel), FailLabel),
+ GetCall = hipe_icode:mk_call([ValVar], maps, get, [KeyVar, MapVar], remote),
+ {TestList, GetList, Env2} = trans_map_query(MapVar, FailLabel, Env1, KVPs),
+ {[Move, IsKeyCall, TrueTest, PassLabel|TestList], [GetCall|GetList], Env2}.
+
+%%
+%% Generates a fail label if necessary when translating put_map_* instructions.
+%%
+gen_put_map_instrs(exists, Op, TempMapVar, Dst, FailLbl, Pairs, Env) ->
+ TrueLabel = mk_label(new),
+ IsMapCode = hipe_icode:mk_type([TempMapVar], map,
+ hipe_icode:label_name(TrueLabel), map_label(FailLbl)),
+ DstMapVar = mk_var(Dst),
+ {ReturnLbl, PutInstructions, Env1}
+ = case Op of
+ assoc ->
+ trans_put_map_assoc(TempMapVar, DstMapVar, Pairs, Env, []);
+ exact ->
+ trans_put_map_exact(TempMapVar, DstMapVar,
+ map_label(FailLbl), Pairs, Env, [])
+ end,
+ {[IsMapCode, TrueLabel, PutInstructions, ReturnLbl], Env1};
+gen_put_map_instrs(new, Op, TempMapVar, Dst, new, Pairs, Env) ->
+ TrueLabel = mk_label(new),
+ FailLbl = mk_label(new),
+ IsMapCode = hipe_icode:mk_type([TempMapVar], map,
+ hipe_icode:label_name(TrueLabel),
+ hipe_icode:label_name(FailLbl)),
+ DstMapVar = mk_var(Dst),
+ {ReturnLbl, PutInstructions, Env1}
+ = case Op of
+ assoc ->
+ trans_put_map_assoc(TempMapVar, DstMapVar, Pairs, Env, []);
+ exact ->
+ trans_put_map_exact(TempMapVar, DstMapVar,
+ hipe_icode:label_name(FailLbl), Pairs, Env, [])
+ end,
+ Fail = hipe_icode:mk_fail([hipe_icode:mk_const(badarg)], error),
+ {[IsMapCode, TrueLabel, PutInstructions, FailLbl, Fail, ReturnLbl], Env1}.
+
+%%-----------------------------------------------------------------------
+%% This function generates the instructions needed to insert several
+%% (Key, Value) pairs into an existing map, each recursive call inserts
+%% one (Key, Value) pair.
+%%-----------------------------------------------------------------------
+trans_put_map_assoc(MapVar, DestMapVar, [], Env, Acc) ->
+ MoveToReturnVar = hipe_icode:mk_move(DestMapVar, MapVar),
+ ReturnLbl = mk_label(new),
+ GotoReturn = hipe_icode:mk_goto(hipe_icode:label_name(ReturnLbl)),
+ {ReturnLbl, lists:reverse([GotoReturn, MoveToReturnVar | Acc]), Env};
+trans_put_map_assoc(MapVar, DestMapVar, [Key, Value | Rest], Env, Acc) ->
+ {MoveKey, KeyVar, Env1} = mk_move_and_var(Key, Env),
+ {MoveVal, ValVar, Env2} = mk_move_and_var(Value, Env1),
+ BifCall = hipe_icode:mk_call([MapVar], maps, put,
+ [KeyVar, ValVar, MapVar], remote),
+ trans_put_map_assoc(MapVar, DestMapVar, Rest, Env2,
+ [BifCall, MoveVal, MoveKey | Acc]).
+
+%%-----------------------------------------------------------------------
+%% This function generates the instructions needed to update several
+%% (Key, Value) pairs in an existing map, each recursive call inserts
+%% one (Key, Value) pair.
+%%-----------------------------------------------------------------------
+trans_put_map_exact(MapVar, DestMapVar, _FLbl, [], Env, Acc) ->
+ MoveToReturnVar = hipe_icode:mk_move(DestMapVar, MapVar),
+ ReturnLbl = mk_label(new),
+ GotoReturn = hipe_icode:mk_goto(hipe_icode:label_name(ReturnLbl)),
+ {ReturnLbl, lists:reverse([GotoReturn, MoveToReturnVar | Acc]), Env};
+trans_put_map_exact(MapVar, DestMapVar, FLbl, [Key, Value | Rest], Env, Acc) ->
+ SuccLbl = mk_label(new),
+ {MoveKey, KeyVar, Env1} = mk_move_and_var(Key, Env),
+ {MoveVal, ValVar, Env2} = mk_move_and_var(Value, Env1),
+ IsKey = hipe_icode:mk_new_var(),
+ BifCallIsKey = hipe_icode:mk_call([IsKey], maps, is_key,
+ [KeyVar, MapVar], remote),
+ IsKeyTest = hipe_icode:mk_if('=:=', [IsKey, hipe_icode:mk_const(true)],
+ hipe_icode:label_name(SuccLbl), FLbl),
+ BifCallPut = hipe_icode:mk_call([MapVar], maps, put,
+ [KeyVar, ValVar, MapVar], remote),
+ Acc1 = [BifCallPut, SuccLbl, IsKeyTest, BifCallIsKey, MoveVal, MoveKey | Acc],
+ trans_put_map_exact(MapVar, DestMapVar, FLbl, Rest, Env2, Acc1).
+
%%-----------------------------------------------------------------------
%% trans_puts(Code, Environment) ->
%% {Movs, Code, Vars, NewEnv}
diff --git a/lib/hipe/icode/hipe_icode.erl b/lib/hipe/icode/hipe_icode.erl
index 0e651a351c..7b3d087e2d 100644
--- a/lib/hipe/icode/hipe_icode.erl
+++ b/lib/hipe/icode/hipe_icode.erl
@@ -503,7 +503,6 @@
enter_args_update/2,
enter_type/1,
is_enter/1,
-
mk_return/1, %% mk_return(Vars)
%% mk_fail/1, %% mk_fail(Args) class = exit
@@ -606,6 +605,12 @@
-export([highest_var/1, highest_label/1]).
+%%
+%% Exported types
+%%
+
+-export_type([icode/0]).
+
%%---------------------------------------------------------------------
%%
%% Icode
@@ -614,7 +619,7 @@
-spec mk_icode(mfa(), [icode_var()], boolean(), boolean(), [icode_instr()],
{non_neg_integer(),non_neg_integer()},
- {icode_lbl(),icode_lbl()}) -> #icode{}.
+ {icode_lbl(),icode_lbl()}) -> icode().
mk_icode(Fun, Params, IsClosure, IsLeaf, Code, VarRange, LabelRange) ->
#icode{'fun'=Fun, params=Params, code=Code,
is_closure=IsClosure,
@@ -1434,8 +1439,8 @@ subst1([_|Pairs], I) -> subst1(Pairs, I).
%%
%% @doc Returns the successors of an Icode instruction.
%% In CFG form only branch instructions have successors,
-%% but in linear form other instructions like e.g. moves and
-%% others might be the last instruction of some basic block.
+%% but in linear form other instructions like e.g. moves
+%% might be the last instruction of some basic block.
%%
-spec successors(icode_instr()) -> [icode_lbl()].
diff --git a/lib/hipe/icode/hipe_icode.hrl b/lib/hipe/icode/hipe_icode.hrl
index 060493e61e..46c04beb40 100644
--- a/lib/hipe/icode/hipe_icode.hrl
+++ b/lib/hipe/icode/hipe_icode.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -61,8 +61,8 @@
| 'op_exact_eqeq_2' | 'suspend_msg_timeout'.
-type icode_type_test() :: 'atom' | 'bignum' | 'binary' | 'bitstr' | 'boolean'
- | 'cons' | 'fixnum' | 'float'
- | 'function' | 'function2' | 'integer' | 'list' | 'nil'
+ | 'cons' | 'fixnum' | 'float' | 'function'
+ | 'function2' | 'integer' | 'list' | 'map' | 'nil'
| 'number' | 'pid' | 'port' | 'reference' | 'tuple'
| {'atom', atom()} | {'integer', integer()}
| {'record', atom(), non_neg_integer()}
@@ -108,7 +108,6 @@
length :: non_neg_integer(),
cases :: [icode_switch_case()]}).
-
-record(icode_type, {test :: icode_type_test(),
args :: [icode_term_arg()],
true_label :: icode_lbl(),
@@ -178,5 +177,6 @@
var_range :: {non_neg_integer(), non_neg_integer()},
label_range :: {icode_lbl(), icode_lbl()},
info = [] :: icode_info()}).
+-type icode() :: #icode{}.
%%---------------------------------------------------------------------
diff --git a/lib/hipe/icode/hipe_icode_fp.erl b/lib/hipe/icode/hipe_icode_fp.erl
index c0cd9bd2d1..38b3881a77 100644
--- a/lib/hipe/icode/hipe_icode_fp.erl
+++ b/lib/hipe/icode/hipe_icode_fp.erl
@@ -424,7 +424,7 @@ redirect_phis([I|Is] = Code, OldFrom, NewFrom, Acc) ->
NewI = hipe_icode:phi_redirect_pred(I, OldFrom, NewFrom),
redirect_phis(Is, OldFrom, NewFrom, [NewI|Acc]);
_ ->
- lists:reverse(Acc) ++ Code
+ lists:reverse(Acc, Code)
end;
redirect_phis([], _OldFrom, _NewFrom, Acc) ->
lists:reverse(Acc).
diff --git a/lib/hipe/icode/hipe_icode_mulret.erl b/lib/hipe/icode/hipe_icode_mulret.erl
index 2402bad42c..99522f6430 100644
--- a/lib/hipe/icode/hipe_icode_mulret.erl
+++ b/lib/hipe/icode/hipe_icode_mulret.erl
@@ -1166,9 +1166,9 @@ printCallList([]) -> io:format("~n").
%% removeUnElems([#icode_call{'fun'={unsafe_element,_}, args=Var}|List], Var, Res) ->
%% removeUnElems(List, Var, Res);
%% removeUnElems([I=#icode_move{dst=Var}|List], [Var], Res) ->
-%% lists:reverse(Res) ++ [I|List];
+%% lists:reverse(Res, [I|List]);
%% removeUnElems([I=#icode_call{dstlist=Var}|List], Var, Res) ->
-%% lists:reverse(Res) ++ [I|List];
+%% lists:reverse(Res, [I|List]);
%% removeUnElems([I|List], Var, Res) ->
%% removeUnElems(List, Var, [I|Res]);
%% removeUnElems([], _, Res) -> lists:reverse(Res).
@@ -1187,7 +1187,7 @@ printCallList([]) -> io:format("~n").
%% false ->
%% case lists:member(Var, Defs) of
%% true ->
-%% lists:reverse(Res) ++ [I|List];
+%% lists:reverse(Res, [I|List]);
%% false ->
%% removeUnElems(List, Var, [I|Res])
%% end
@@ -1195,7 +1195,7 @@ printCallList([]) -> io:format("~n").
%% false ->
%% case lists:member(Var, Defs) of
%% true ->
-%% lists:reverse(Res) ++ [I|List];
+%% lists:reverse(Res, [I|List]);
%% false ->
%% removeUnElems(List, Var, [I|Res])
%% end
@@ -1203,7 +1203,7 @@ printCallList([]) -> io:format("~n").
%% false ->
%% case lists:member(Var, Defs) of
%% true ->
-%% lists:reverse(Res) ++ [I|List];
+%% lists:reverse(Res, [I|List]);
%% false ->
%% removeUnElems(List, Var, [I|Res])
%% end
@@ -1248,16 +1248,16 @@ printCallList([]) -> io:format("~n").
%% modifyCode([I|Code], Var, Res) ->
%% case scanInstr(I, Var) of
%% {move, Arity, VarLst} ->
-%% Code2 = [#icode_return{vars=VarLst}, I |lists:reverse(Res) ++ Code],
+%% Code2 = [#icode_return{vars=VarLst}, I |lists:reverse(Res, Code)],
%% {Arity, lists:reverse(Code2)};
%% {mktuple, Arity, VarLst} ->
-%% Code2 = [#icode_return{vars=VarLst}|lists:reverse(Res) ++ Code],
+%% Code2 = [#icode_return{vars=VarLst}|lists:reverse(Res, Code)],
%% {Arity, lists:reverse(Code2)};
%% other ->
%% modifyCode(Code, Var, [I|Res])
%% end;
%% modifyCode([], Var, Res) ->
-%% {1, lists:reverse(Res) ++ [#icode_return{vars=Var}]}.
+%% {1, lists:reverse(Res, [#icode_return{vars=Var}]}.
%% scanInstr(#icode_call{dstlist=Var, 'fun'=mktuple, args=Lst}, Var) ->
%% {mktuple, length(Lst), Lst};
diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl
index 65876b83ea..ebeb5e2c10 100644
--- a/lib/hipe/icode/hipe_icode_type.erl
+++ b/lib/hipe/icode/hipe_icode_type.erl
@@ -84,15 +84,15 @@
t_fun/0, t_fun/1, t_fun/2, t_fun_args/1, t_fun_arity/1,
t_inf/2, t_inf_lists/2, t_integer/0,
t_integer/1, t_is_atom/1, t_is_any/1,
- t_is_binary/1, t_is_bitstr/1, t_is_bitwidth/1, t_is_boolean/1,
- t_is_fixnum/1, t_is_cons/1,
+ t_is_binary/1, t_is_bitstr/1, t_is_bitwidth/1,
+ t_is_boolean/1, t_is_fixnum/1, t_is_cons/1, t_is_map/1,
t_is_maybe_improper_list/1, t_is_equal/2, t_is_float/1,
t_is_fun/1, t_is_integer/1, t_is_non_neg_integer/1,
t_is_number/1, t_is_matchstate/1,
- t_is_nil/1, t_is_none/1, t_is_port/1, t_is_pid/1,
+ t_is_none/1, t_is_port/1, t_is_pid/1,
t_is_reference/1, t_is_subtype/2, t_is_tuple/1,
t_limit/2, t_matchstate_present/1, t_matchstate/0,
- t_matchstate_slots/1, t_maybe_improper_list/0,
+ t_matchstate_slots/1, t_maybe_improper_list/0, t_map/0,
t_nil/0, t_none/0, t_number/0, t_number/1, t_number_vals/1,
t_pid/0, t_port/0, t_reference/0, t_subtract/2, t_sup/2,
t_to_tlist/1, t_tuple/0, t_tuple/1, t_tuple_sizes/1]).
@@ -213,7 +213,7 @@ analyse_blocks(Work, State, MFA) ->
{NewState, NewLabels} =
try analyse_block(Label, Info, State)
catch throw:none_type ->
- %% io:format("received none type at label: ~p~n",[Label]),
+ %% io:format("received none type at label: ~p~n", [Label]),
{State,[]}
end,
NewWork2 = add_work(NewWork, NewLabels),
@@ -265,7 +265,7 @@ analyse_insn(I, Info, LookupFun) ->
do_move(I, Info);
#icode_call{} ->
NewInfo = do_call(I, Info, LookupFun),
- %%io:format("Analysing Call: ~w~n~w~n", [I,NewInfo]),
+ %% io:format("Analysing Call: ~w~n~w~n", [I, NewInfo]),
update_call_arguments(I, NewInfo);
#icode_phi{} ->
Type = t_limit(join_list(hipe_icode:args(I), Info), ?TYPE_DEPTH),
@@ -788,16 +788,16 @@ test_record(Atom, Size, Var, VarInfo, TrueLab, FalseLab, Info) ->
end.
test_type(Test, Type) ->
- %%io:format("Test is: ~w\n", [Test]),
- %%io:format("Type is: ~s\n", [format_type(Type)]),
+ %% io:format("Test is: ~w\n", [Test]),
+ %% io:format("Type is: ~s\n", [format_type(Type)]),
Ans =
case t_is_any(Type) of
true -> maybe;
false ->
TrueTest = true_branch_info(Test),
Inf = t_inf(TrueTest, Type),
- %%io:format("TrueTest is: ~s\n", [format_type(TrueTest)]),
- %%io:format("Inf is: ~s\n", [format_type(Inf)]),
+ %% io:format("TrueTest is: ~s\n", [format_type(TrueTest)]),
+ %% io:format("Inf is: ~s\n", [format_type(Inf)]),
case t_is_equal(Type, Inf) of
true ->
not t_is_none(Type);
@@ -895,11 +895,12 @@ test_type0(boolean, T) ->
t_is_boolean(T);
test_type0(list, T) ->
t_is_maybe_improper_list(T);
-test_type0(cons, T) ->
- t_is_cons(T);
-test_type0(nil, T) ->
- t_is_nil(T).
-
+%% test_type0(cons, T) ->
+%% t_is_cons(T);
+%% test_type0(nil, T) ->
+%% t_is_nil(T).
+test_type0(map, T) ->
+ t_is_map(T).
true_branch_info(integer) ->
t_integer();
@@ -931,22 +932,24 @@ true_branch_info(reference) ->
t_reference();
true_branch_info(function) ->
t_fun();
-true_branch_info(cons) ->
- t_cons();
-true_branch_info(nil) ->
- t_nil();
+%% true_branch_info(cons) ->
+%% t_cons();
+%% true_branch_info(nil) ->
+%% t_nil();
true_branch_info(boolean) ->
t_boolean();
+true_branch_info(map) ->
+ t_map();
true_branch_info(T) ->
- exit({?MODULE,unknown_typetest,T}).
+ exit({?MODULE, unknown_typetest, T}).
%% _________________________________________________________________
%%
%% Remove the redundant type tests. If a test is removed, the trace
-%% that isn't taken is explicitly removed from the CFG to simpilify
+%% that isn't taken is explicitly removed from the CFG to simplify
%% the handling of Phi nodes. If a Phi node is left and at least one
-%% branch into it has disappeared, the SSA propagation pass can't
+%% branch into it has disappeared, the SSA propagation pass cannot
%% handle it.
%%
%% If the CFG has changed at the end of this pass, the analysis is
diff --git a/lib/hipe/llvm/Makefile b/lib/hipe/llvm/Makefile
new file mode 100644
index 0000000000..92f378924a
--- /dev/null
+++ b/lib/hipe/llvm/Makefile
@@ -0,0 +1,109 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2001-2014. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+ifndef EBIN
+EBIN = ../ebin
+endif
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(HIPE_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+ifdef HIPE_ENABLED
+HIPE_MODULES = hipe_rtl_to_llvm \
+ hipe_llvm \
+ elf_format \
+ hipe_llvm_main \
+ hipe_llvm_merge \
+ hipe_llvm_liveness
+else
+HIPE_MODULES =
+endif
+
+MODULES = $(HIPE_MODULES)
+
+HRL_FILES= elf_format.hrl elf32_format.hrl elf64_format.hrl \
+ hipe_llvm_arch.hrl
+ERL_FILES= $(MODULES:%=%.erl)
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+# APP_FILE=
+# App_SRC= $(APP_FILE).src
+# APP_TARGET= $(EBIN)/$(APP_FILE)
+#
+# APPUP_FILE=
+# APPUP_SRC= $(APPUP_FILE).src
+# APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+# ----------------------------------------------------
+# FLAGS: Please keep +inline below
+# ----------------------------------------------------
+
+include ../native.mk
+
+ERL_COMPILE_FLAGS += +inline #+warn_missing_spec
+
+# if in 32 bit backend define BIT32 symbol
+ARCH = $(shell echo $(TARGET) | sed 's/^\(x86_64\)-.*/64bit/')
+ifneq ($(ARCH), 64bit)
+ERL_COMPILE_FLAGS += -DBIT32
+endif
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+docs:
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core erl_crash.dump
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/llvm
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/llvm
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
diff --git a/lib/hipe/llvm/elf32_format.hrl b/lib/hipe/llvm/elf32_format.hrl
new file mode 100644
index 0000000000..af1d95bf5b
--- /dev/null
+++ b/lib/hipe/llvm/elf32_format.hrl
@@ -0,0 +1,59 @@
+%% -*- erlang-indent-level: 2 -*-
+
+%%% @copyright 2011-2014 Yiannis Tsiouris <[email protected]>,
+%%% Chris Stavrakakis <[email protected]>
+%%% @author Yiannis Tsiouris <[email protected]>
+%%% [http://www.softlab.ntua.gr/~gtsiour/]
+
+%%% @doc This header file contains very very useful macros for handling
+%%% various segments of an ELF-32 formated object file, such as sizes,
+%%% offsets and predefined constants. For further information about
+%%% each field take a quick look at
+%%% "[http://www.sco.com/developers/gabi/latest/contents.html]"
+%%% that contain the current HP/Intel definition of the ELF object
+%%% file format.
+
+%%------------------------------------------------------------------------------
+%% ELF-32 Data Types (in bytes)
+%%------------------------------------------------------------------------------
+-define(ELF_ADDR_SIZE, 4).
+-define(ELF_OFF_SIZE, 4).
+-define(ELF_HALF_SIZE, 2).
+-define(ELF_WORD_SIZE, 4).
+-define(ELF_SWORD_SIZE, 4).
+-define(ELF_XWORD_SIZE, ?ELF_WORD_SIZE). % for compatibility
+-define(ELF_SXWORD_SIZE, ?ELF_WORD_SIZE).
+-define(ELF_UNSIGNED_CHAR_SIZE, 1).
+
+%%------------------------------------------------------------------------------
+%% ELF-32 Symbol Table Entries
+%%------------------------------------------------------------------------------
+%% Precomputed offset for Symbol Table entries in SymTab binary (needed because
+%% of the different offsets in 32 and 64 bit formats).
+-define(ST_NAME_OFFSET, 0).
+-define(ST_VALUE_OFFSET, (?ST_NAME_OFFSET + ?ST_NAME_SIZE) ).
+-define(ST_SIZE_OFFSET, (?ST_VALUE_OFFSET + ?ST_VALUE_SIZE) ).
+-define(ST_INFO_OFFSET, (?ST_SIZE_OFFSET + ?ST_SIZE_SIZE) ).
+-define(ST_OTHER_OFFSET, (?ST_INFO_OFFSET + ?ST_INFO_SIZE) ).
+-define(ST_SHNDX_OFFSET, (?ST_OTHER_OFFSET + ?ST_OTHER_SIZE) ).
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Relocation Entries
+%%------------------------------------------------------------------------------
+%% Useful macros to extract information from r_info field
+-define(ELF_R_SYM(I), (I bsr 8) ).
+-define(ELF_R_TYPE(I), (I band 16#ff) ).
+-define(ELF_R_INFO(S, T), ((S bsl 8) + (T band 16#ff)) ).
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Program Header Table
+%%------------------------------------------------------------------------------
+%% Offsets of various fields in a Program Header Table entry binary.
+-define(P_TYPE_OFFSET, 0).
+-define(P_OFFSET_OFFSET, (?P_FLAGS_OFFSET + ?P_FLAGS_SIZE) ).
+-define(P_VADDR_OFFSET, (?P_OFFSET_OFFSET + ?P_OFFSET_SIZE) ).
+-define(P_PADDR_OFFSET, (?P_VADDR_OFFSET + ?P_VADDR_SIZE) ).
+-define(P_FILESZ_OFFSET, (?P_PVADDR_OFFSET + ?P_PVADDR_SIZE) ).
+-define(P_MEMSZ_OFFSET, (?P_FILESZ_OFFSET + ?P_FILESZ_SIZE) ).
+-define(P_FLAGS_OFFSET, (?P_TYPE_OFFSET + ?P_TYPE_SIZE) ).
+-define(P_ALIGN_OFFSET, (?P_MEMSZ_OFFSET + ?P_MEMSZ_SIZE) ).
diff --git a/lib/hipe/llvm/elf64_format.hrl b/lib/hipe/llvm/elf64_format.hrl
new file mode 100644
index 0000000000..794746ffdc
--- /dev/null
+++ b/lib/hipe/llvm/elf64_format.hrl
@@ -0,0 +1,58 @@
+%% -*- erlang-indent-level: 2 -*-
+
+%%% @copyright 2011-2014 Yiannis Tsiouris <[email protected]>,
+%%% Chris Stavrakakis <[email protected]>
+%%% @author Yiannis Tsiouris <[email protected]>
+%%% [http://www.softlab.ntua.gr/~gtsiour/]
+
+%%% @doc This header file contains very very useful macros for handling
+%%% various segments of an ELF-64 formated object file, such as sizes,
+%%% offsets and predefined constants. For further information about
+%%% each field take a quick look at
+%%% "[http://downloads.openwatcom.org/ftp/devel/docs/elf-64-gen.pdf]"
+%%% that contain the current HP/Intel definition of the ELF object
+%%% file format.
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Data Types (in bytes)
+%%------------------------------------------------------------------------------
+-define(ELF_ADDR_SIZE, 8).
+-define(ELF_OFF_SIZE, 8).
+-define(ELF_HALF_SIZE, 2).
+-define(ELF_WORD_SIZE, 4).
+-define(ELF_SWORD_SIZE, 4).
+-define(ELF_XWORD_SIZE, 8).
+-define(ELF_SXWORD_SIZE, 8).
+-define(ELF_UNSIGNED_CHAR_SIZE, 1).
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Symbol Table Entries
+%%------------------------------------------------------------------------------
+%% Precomputed offset for Symbol Table entries in SymTab binary
+-define(ST_NAME_OFFSET, 0).
+-define(ST_INFO_OFFSET, (?ST_NAME_OFFSET + ?ST_NAME_SIZE) ).
+-define(ST_OTHER_OFFSET, (?ST_INFO_OFFSET + ?ST_INFO_SIZE) ).
+-define(ST_SHNDX_OFFSET, (?ST_OTHER_OFFSET + ?ST_OTHER_SIZE) ).
+-define(ST_VALUE_OFFSET, (?ST_SHNDX_OFFSET + ?ST_SHNDX_SIZE) ).
+-define(ST_SIZE_OFFSET, (?ST_VALUE_OFFSET + ?ST_VALUE_SIZE) ).
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Relocation Entries
+%%------------------------------------------------------------------------------
+%% Useful macros to extract information from r_info field
+-define(ELF_R_SYM(I), (I bsr 32) ).
+-define(ELF_R_TYPE(I), (I band 16#ffffffff) ).
+-define(ELF_R_INFO(S, T), ((S bsl 32) + (T band 16#ffffffff)) ).
+
+%%------------------------------------------------------------------------------
+%% ELF-64 Program Header Table
+%%------------------------------------------------------------------------------
+%% Offsets of various fields in a Program Header Table entry binary.
+-define(P_TYPE_OFFSET, 0).
+-define(P_FLAGS_OFFSET, (?P_TYPE_OFFSET + ?P_TYPE_SIZE) ).
+-define(P_OFFSET_OFFSET, (?P_FLAGS_OFFSET + ?P_FLAGS_SIZE) ).
+-define(P_VADDR_OFFSET, (?P_OFFSET_OFFSET + ?P_OFFSET_SIZE) ).
+-define(P_PADDR_OFFSET, (?P_VADDR_OFFSET + ?P_VADDR_SIZE) ).
+-define(P_FILESZ_OFFSET, (?P_PVADDR_OFFSET + ?P_PVADDR_SIZE) ).
+-define(P_MEMSZ_OFFSET, (?P_FILESZ_OFFSET + ?P_FILESZ_SIZE) ).
+-define(P_ALIGN_OFFSET, (?P_MEMSZ_OFFSET + ?P_MEMSZ_SIZE) ).
diff --git a/lib/hipe/llvm/elf_format.erl b/lib/hipe/llvm/elf_format.erl
new file mode 100644
index 0000000000..260da9b5e6
--- /dev/null
+++ b/lib/hipe/llvm/elf_format.erl
@@ -0,0 +1,790 @@
+%% -*- erlang-indent-level: 2 -*-
+
+%%% @copyright 2011-2014 Yiannis Tsiouris <[email protected]>,
+%%% Chris Stavrakakis <[email protected]>,
+%%% Kostis Sagonas <[email protected]>
+%%% @author Yiannis Tsiouris <[email protected]>
+%%% [http://www.softlab.ntua.gr/~gtsiour/]
+
+%%% @doc This module contains functions for extracting various pieces of
+%%% information from an ELF formated Object file. To fully understand
+%%% the ELF format and the use of these functions please read
+%%% "[http://www.linuxjournal.com/article/1060?page=0,0]" carefully.
+
+-module(elf_format).
+
+-export([get_tab_entries/1,
+ %% Relocations
+ get_rodata_relocs/1,
+ get_text_relocs/1,
+ extract_rela/2,
+ get_rela_addends/1,
+ %% Note
+ extract_note/2,
+ %% Executable code
+ extract_text/1,
+ %% GCC Exception Table
+ get_exn_handlers/1,
+ %% Misc.
+ set_architecture_flag/1,
+ is64bit/0
+ ]).
+
+-include("elf_format.hrl").
+
+%%------------------------------------------------------------------------------
+%% Types
+%%------------------------------------------------------------------------------
+
+-type elf() :: binary().
+
+-type lp() :: non_neg_integer(). % landing pad
+-type num() :: non_neg_integer().
+-type index() :: non_neg_integer().
+-type offset() :: non_neg_integer().
+-type size() :: non_neg_integer().
+-type start() :: non_neg_integer().
+
+-type info() :: index().
+-type nameoff() :: offset().
+-type valueoff() :: offset().
+
+-type name() :: string().
+-type name_size() :: {name(), size()}.
+-type name_sizes() :: [name_size()].
+
+%%------------------------------------------------------------------------------
+%% Abstract Data Types and Accessors for ELF Structures.
+%%------------------------------------------------------------------------------
+
+%% File header
+-record(elf_ehdr, {ident, % ELF identification
+ type, % Object file type
+ machine, % Machine Type
+ version, % Object file version
+ entry, % Entry point address
+ phoff, % Program header offset
+ shoff :: offset(), % Section header offset
+ flags, % Processor-specific flags
+ ehsize :: size(), % ELF header size
+ phentsize :: size(), % Size of program header entry
+ phnum :: num(), % Number of program header entries
+ shentsize :: size(), % Size of section header entry
+ shnum :: num(), % Number of section header entries
+ shstrndx :: index() % Section name string table index
+ }).
+-type elf_ehdr() :: #elf_ehdr{}.
+
+-record(elf_ehdr_ident, {class, % File class
+ data, % Data encoding
+ version, % File version
+ osabi, % OS/ABI identification
+ abiversion, % ABI version
+ pad, % Start of padding bytes
+ nident % Size of e_ident[]
+ }).
+%% -type elf_ehdr_ident() :: #elf_ehdr_ident{}.
+
+%% Section header entries
+-record(elf_shdr, {name, % Section name
+ type, % Section type
+ flags, % Section attributes
+ addr, % Virtual address in memory
+ offset :: offset(), % Offset in file
+ size :: size(), % Size of section
+ link, % Link to other section
+ info, % Miscellaneous information
+ addralign, % Address align boundary
+ entsize % Size of entries, if section has table
+ }).
+%% -type elf_shdr() :: #elf_shdr{}.
+
+%% Symbol table entries
+-record(elf_sym, {name :: nameoff(), % Symbol name
+ info, % Type and Binding attributes
+ other, % Reserved
+ shndx, % Section table index
+ value :: valueoff(), % Symbol value
+ size :: size() % Size of object
+ }).
+-type elf_sym() :: #elf_sym{}.
+
+%% Relocations
+-record(elf_rel, {r_offset :: offset(), % Address of reference
+ r_info :: info() % Symbol index and type of relocation
+ }).
+-type elf_rel() :: #elf_rel{}.
+
+-record(elf_rela, {r_offset :: offset(), % Address of reference
+ r_info :: info(), % Symbol index and type of relocation
+ r_addend :: offset() % Constant part of expression
+ }).
+-type elf_rela() :: #elf_rela{}.
+
+%% %% Program header table
+%% -record(elf_phdr, {type, % Type of segment
+%% flags, % Segment attributes
+%% offset, % Offset in file
+%% vaddr, % Virtual address in memory
+%% paddr, % Reserved
+%% filesz, % Size of segment in file
+%% memsz, % Size of segment in memory
+%% align % Alignment of segment
+%% }).
+
+%% %% GCC exception table
+%% -record(elf_gccexntab, {lpbenc, % Landing pad base encoding
+%% lpbase, % Landing pad base
+%% ttenc, % Type table encoding
+%% ttoff, % Type table offset
+%% csenc, % Call-site table encoding
+%% cstabsize, % Call-site table size
+%% cstab :: cstab() % Call-site table
+%% }).
+%% -type elf_gccexntab() :: #elf_gccexntab{}.
+
+-record(elf_gccexntab_callsite, {start :: start(), % Call-site start
+ size :: size(), % Call-site size
+ lp :: lp(), % Call-site landing pad
+ % (exception handler)
+ onaction % On action (e.g. cleanup)
+ }).
+%% -type elf_gccexntab_callsite() :: #elf_gccexntab_callsite{}.
+
+%%------------------------------------------------------------------------------
+%% Accessor Functions
+%%------------------------------------------------------------------------------
+
+%% File header
+%% -spec mk_ehdr(...) -> elf_ehrd().
+mk_ehdr(Ident, Type, Machine, Version, Entry, Phoff, Shoff, Flags, Ehsize,
+ Phentsize, Phnum, Shentsize, Shnum, Shstrndx) ->
+ #elf_ehdr{ident = Ident, type = Type, machine = Machine, version = Version,
+ entry = Entry, phoff = Phoff, shoff = Shoff, flags = Flags,
+ ehsize = Ehsize, phentsize = Phentsize, phnum = Phnum,
+ shentsize = Shentsize, shnum = Shnum, shstrndx = Shstrndx}.
+
+%% -spec ehdr_shoff(elf_ehdr()) -> offset().
+%% ehdr_shoff(#elf_ehdr{shoff = Offset}) -> Offset.
+%%
+%% -spec ehdr_shentsize(elf_ehdr()) -> size().
+%% ehdr_shentsize(#elf_ehdr{shentsize = Size}) -> Size.
+%%
+%% -spec ehdr_shnum(elf_ehdr()) -> num().
+%% ehdr_shnum(#elf_ehdr{shnum = Num}) -> Num.
+%%
+%% -spec ehdr_shstrndx(elf_ehdr()) -> index().
+%% ehdr_shstrndx(#elf_ehdr{shstrndx = Index}) -> Index.
+
+
+%%-spec mk_ehdr_ident(...) -> elf_ehdr_ident().
+mk_ehdr_ident(Class, Data, Version, OsABI, AbiVersion, Pad, Nident) ->
+ #elf_ehdr_ident{class = Class, data = Data, version = Version, osabi = OsABI,
+ abiversion = AbiVersion, pad = Pad, nident = Nident}.
+
+%%%-------------------------
+%%% Section header entries
+%%%-------------------------
+mk_shdr(Name, Type, Flags, Addr, Offset, Size, Link, Info, AddrAlign, EntSize) ->
+ #elf_shdr{name = Name, type = Type, flags = Flags, addr = Addr,
+ offset = Offset, size = Size, link = Link, info = Info,
+ addralign = AddrAlign, entsize = EntSize}.
+
+%% -spec shdr_offset(elf_shdr()) -> offset().
+%% shdr_offset(#elf_shdr{offset = Offset}) -> Offset.
+%%
+%% -spec shdr_size(elf_shdr()) -> size().
+%% shdr_size(#elf_shdr{size = Size}) -> Size.
+
+%%%-------------------------
+%%% Symbol Table Entries
+%%%-------------------------
+mk_sym(Name, Info, Other, Shndx, Value, Size) ->
+ #elf_sym{name = Name, info = Info, other = Other,
+ shndx = Shndx, value = Value, size = Size}.
+
+-spec sym_name(elf_sym()) -> nameoff().
+sym_name(#elf_sym{name = Name}) -> Name.
+
+%% -spec sym_value(elf_sym()) -> valueoff().
+%% sym_value(#elf_sym{value = Value}) -> Value.
+%%
+%% -spec sym_size(elf_sym()) -> size().
+%% sym_size(#elf_sym{size = Size}) -> Size.
+
+%%%-------------------------
+%%% Relocations
+%%%-------------------------
+-spec mk_rel(offset(), info()) -> elf_rel().
+mk_rel(Offset, Info) ->
+ #elf_rel{r_offset = Offset, r_info = Info}.
+
+%% The following two functions capitalize on the fact that the two kinds of
+%% relocation records (for 32- and 64-bit architectures have similar structure.
+
+-spec r_offset(elf_rel() | elf_rela()) -> offset().
+r_offset(#elf_rel{r_offset = Offset}) -> Offset;
+r_offset(#elf_rela{r_offset = Offset}) -> Offset.
+
+-spec r_info(elf_rel() | elf_rela()) -> info().
+r_info(#elf_rel{r_info = Info}) -> Info;
+r_info(#elf_rela{r_info = Info}) -> Info.
+
+-spec mk_rela(offset(), info(), offset()) -> elf_rela().
+mk_rela(Offset, Info, Addend) ->
+ #elf_rela{r_offset = Offset, r_info = Info, r_addend = Addend}.
+
+-spec rela_addend(elf_rela()) -> offset().
+rela_addend(#elf_rela{r_addend = Addend}) -> Addend.
+
+%% %%%-------------------------
+%% %%% GCC exception table
+%% %%%-------------------------
+%% -type cstab() :: [elf_gccexntab_callsite()].
+%%
+%% mk_gccexntab(LPbenc, LPbase, TTenc, TToff, CSenc, CStabsize, CStab) ->
+%% #elf_gccexntab{lpbenc = LPbenc, lpbase = LPbase, ttenc = TTenc,
+%% ttoff = TToff, csenc = CSenc, cstabsize = CStabsize,
+%% cstab = CStab}.
+%%
+%% -spec gccexntab_cstab(elf_gccexntab()) -> cstab().
+%% gccexntab_cstab(#elf_gccexntab{cstab = CSTab}) -> CSTab.
+
+mk_gccexntab_callsite(Start, Size, LP, Action) ->
+ #elf_gccexntab_callsite{start = Start, size=Size, lp=LP, onaction=Action}.
+
+%% -spec gccexntab_callsite_start(elf_gccexntab_callsite()) -> start().
+%% gccexntab_callsite_start(#elf_gccexntab_callsite{start = Start}) -> Start.
+%%
+%% -spec gccexntab_callsite_size(elf_gccexntab_callsite()) -> size().
+%% gccexntab_callsite_size(#elf_gccexntab_callsite{size = Size}) -> Size.
+%%
+%% -spec gccexntab_callsite_lp(elf_gccexntab_callsite()) -> lp().
+%% gccexntab_callsite_lp(#elf_gccexntab_callsite{lp = LP}) -> LP.
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate the ELF File Header
+%%------------------------------------------------------------------------------
+
+%% @doc Extracts the File Header from an ELF formatted object file. Also sets
+%% the ELF class variable in the process dictionary (used by many functions
+%% in this and hipe_llvm_main modules).
+-spec extract_header(elf()) -> elf_ehdr().
+extract_header(Elf) ->
+ Ehdr_bin = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE),
+ << %% Structural pattern matching on fields.
+ Ident_bin:?E_IDENT_SIZE/binary,
+ Type:?bits(?E_TYPE_SIZE)/integer-little,
+ Machine:?bits(?E_MACHINE_SIZE)/integer-little,
+ Version:?bits(?E_VERSION_SIZE)/integer-little,
+ Entry:?bits(?E_ENTRY_SIZE)/integer-little,
+ Phoff:?bits(?E_PHOFF_SIZE)/integer-little,
+ Shoff:?bits(?E_SHOFF_SIZE)/integer-little,
+ Flags:?bits(?E_FLAGS_SIZE)/integer-little,
+ Ehsize:?bits(?E_EHSIZE_SIZE)/integer-little,
+ Phentsize:?bits(?E_PHENTSIZE_SIZE)/integer-little,
+ Phnum:?bits(?E_PHNUM_SIZE)/integer-little,
+ Shentsize:?bits(?E_SHENTSIZE_SIZE)/integer-little,
+ Shnum:?bits(?E_SHENTSIZE_SIZE)/integer-little,
+ Shstrndx:?bits(?E_SHSTRNDX_SIZE)/integer-little
+ >> = Ehdr_bin,
+ <<16#7f, $E, $L, $F, Class, Data, Version, Osabi, Abiversion,
+ Pad:6/binary, Nident
+ >> = Ident_bin,
+ Ident = mk_ehdr_ident(Class, Data, Version, Osabi,
+ Abiversion, Pad, Nident),
+ mk_ehdr(Ident, Type, Machine, Version, Entry, Phoff, Shoff, Flags,
+ Ehsize, Phentsize, Phnum, Shentsize, Shnum, Shstrndx).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Section Header Entries
+%%------------------------------------------------------------------------------
+
+%% @doc Extracts the Section Header Table from an ELF formated Object File.
+extract_shdrtab(Elf) ->
+ %% Extract File Header to get info about Section Header Offset (in bytes),
+ %% Entry Size (in bytes) and Number of entries
+ #elf_ehdr{shoff = ShOff, shentsize = ShEntsize, shnum = ShNum} =
+ extract_header(Elf),
+ %% Get actual Section header table (binary)
+ ShdrBin = get_binary_segment(Elf, ShOff, ShNum * ShEntsize),
+ get_shdrtab_entries(ShdrBin, []).
+
+get_shdrtab_entries(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_shdrtab_entries(ShdrBin, Acc) ->
+ <<%% Structural pattern matching on fields.
+ Name:?bits(?SH_NAME_SIZE)/integer-little,
+ Type:?bits(?SH_TYPE_SIZE)/integer-little,
+ Flags:?bits(?SH_FLAGS_SIZE)/integer-little,
+ Addr:?bits(?SH_ADDR_SIZE)/integer-little,
+ Offset:?bits(?SH_OFFSET_SIZE)/integer-little,
+ Size:?bits(?SH_SIZE_SIZE)/integer-little,
+ Link:?bits(?SH_LINK_SIZE)/integer-little,
+ Info:?bits(?SH_INFO_SIZE)/integer-little,
+ Addralign:?bits(?SH_ADDRALIGN_SIZE)/integer-little,
+ Entsize:?bits(?SH_ENTSIZE_SIZE)/integer-little,
+ MoreShdrE/binary
+ >> = ShdrBin,
+ ShdrE = mk_shdr(Name, Type, Flags, Addr, Offset,
+ Size, Link, Info, Addralign, Entsize),
+ get_shdrtab_entries(MoreShdrE, [ShdrE | Acc]).
+
+%% @doc Extracts a specific Entry of a Section Header Table. This function
+%% takes as argument the Section Header Table (`SHdrTab') and the entry's
+%% serial number (`EntryNum') and returns the entry (`shdr').
+get_shdrtab_entry(SHdrTab, EntryNum) ->
+ lists:nth(EntryNum + 1, SHdrTab).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Section Header String Table
+%%------------------------------------------------------------------------------
+
+%% @doc Extracts the Section Header String Table. This section is not a known
+%% ELF Object File section. It is just a "hidden" table storing the
+%% names of all sections that exist in current object file.
+-spec extract_shstrtab(elf()) -> [name()].
+extract_shstrtab(Elf) ->
+ %% Extract Section Name String Table Index
+ #elf_ehdr{shstrndx = ShStrNdx} = extract_header(Elf),
+ ShHdrTab = extract_shdrtab(Elf),
+ %% Extract Section header entry and get actual Section-header String Table
+ #elf_shdr{offset = ShStrOffset, size = ShStrSize} =
+ get_shdrtab_entry(ShHdrTab, ShStrNdx),
+ case get_binary_segment(Elf, ShStrOffset, ShStrSize) of
+ <<>> -> %% Segment empty
+ [];
+ ShStrTab -> %% Convert to string table
+ [Name || {Name, _Size} <- get_names(ShStrTab)]
+ end.
+
+%%------------------------------------------------------------------------------
+
+-spec get_tab_entries(elf()) -> [{name(), valueoff(), size()}].
+get_tab_entries(Elf) ->
+ SymTab = extract_symtab(Elf),
+ Ts = [{Name, Value, Size div ?ELF_XWORD_SIZE}
+ || #elf_sym{name = Name, value = Value, size = Size} <- SymTab,
+ Name =/= 0],
+ {NameIndices, ValueOffs, Sizes} = lists:unzip3(Ts),
+ %% Find the names of the symbols.
+ %% Get string table entries ([{Name, Offset in strtab section}]). Keep only
+ %% relevant entries:
+ StrTab = extract_strtab(Elf),
+ Relevant = [get_strtab_entry(StrTab, Off) || Off <- NameIndices],
+ %% Zip back to {Name, ValueOff, Size}
+ lists:zip3(Relevant, ValueOffs, Sizes).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Symbol Table
+%%------------------------------------------------------------------------------
+
+%% @doc Function that extracts Symbol Table from an ELF Object file.
+extract_symtab(Elf) ->
+ Symtab_bin = extract_segment_by_name(Elf, ?SYMTAB),
+ get_symtab_entries(Symtab_bin, []).
+
+get_symtab_entries(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_symtab_entries(Symtab_bin, Acc) ->
+ <<SymE_bin:?ELF_SYM_SIZE/binary, MoreSymE/binary>> = Symtab_bin,
+ case is64bit() of
+ true ->
+ <<%% Structural pattern matching on fields.
+ Name:?bits(?ST_NAME_SIZE)/integer-little,
+ Info:?bits(?ST_INFO_SIZE)/integer-little,
+ Other:?bits(?ST_OTHER_SIZE)/integer-little,
+ Shndx:?bits(?ST_SHNDX_SIZE)/integer-little,
+ Value:?bits(?ST_VALUE_SIZE)/integer-little,
+ Size:?bits(?ST_SIZE_SIZE)/integer-little
+ >> = SymE_bin;
+ false ->
+ << %% Same fields in different order:
+ Name:?bits(?ST_NAME_SIZE)/integer-little,
+ Value:?bits(?ST_VALUE_SIZE)/integer-little,
+ Size:?bits(?ST_SIZE_SIZE)/integer-little,
+ Info:?bits(?ST_INFO_SIZE)/integer-little,
+ Other:?bits(?ST_OTHER_SIZE)/integer-little,
+ Shndx:?bits(?ST_SHNDX_SIZE)/integer-little
+ >> = SymE_bin
+ end,
+ SymE = mk_sym(Name, Info, Other, Shndx, Value, Size),
+ get_symtab_entries(MoreSymE, [SymE | Acc]).
+
+%% @doc Extracts a specific entry from the Symbol Table (as binary).
+%% This function takes as arguments the Symbol Table (`SymTab')
+%% and the entry's serial number and returns that entry (`sym').
+get_symtab_entry(SymTab, EntryNum) ->
+ lists:nth(EntryNum + 1, SymTab).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate String Table
+%%------------------------------------------------------------------------------
+
+%% @doc Extracts String Table from an ELF formated Object File.
+-spec extract_strtab(elf()) -> [{string(), offset()}].
+extract_strtab(Elf) ->
+ Strtab_bin = extract_segment_by_name(Elf, ?STRTAB),
+ NamesSizes = get_names(Strtab_bin),
+ make_offsets(NamesSizes).
+
+%% @doc Returns the name of the symbol at the given offset. The string table
+%% contains entries of the form {Name, Offset}. If no such offset exists
+%% returns the empty string (`""').
+%% XXX: There might be a bug here because of the "compact" saving the ELF
+%% format uses: e.g. only stores ".rela.text" for ".rela.text" and ".text".
+get_strtab_entry(Strtab, Offset) ->
+ case lists:keyfind(Offset, 2, Strtab) of
+ {Name, Offset} -> Name;
+ false -> ""
+ end.
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Relocations
+%%------------------------------------------------------------------------------
+
+%% @doc This function gets as argument an ELF binary file and returns a list
+%% with all .rela.rodata labels (i.e. constants and literals in code)
+%% or an empty list if no ".rela.rodata" section exists in code.
+-spec get_rodata_relocs(elf()) -> [offset()].
+get_rodata_relocs(Elf) ->
+ case is64bit() of
+ true ->
+ %% Only care about the addends (== offsets):
+ get_rela_addends(extract_rela(Elf, ?RODATA));
+ false ->
+ %% Find offsets hardcoded in ".rodata" entry
+ %%XXX: Treat all 0s as padding and skip them!
+ [SkipPadding || SkipPadding <- extract_rodata(Elf), SkipPadding =/= 0]
+ end.
+
+-spec get_rela_addends([elf_rela()]) -> [offset()].
+get_rela_addends(RelaEntries) ->
+ [rela_addend(E) || E <- RelaEntries].
+
+%% @doc Extract a list of the form `[{SymbolName, Offset}]' with all relocatable
+%% symbols and their offsets in the code from the ".text" section.
+-spec get_text_relocs(elf()) -> [{name(), offset()}].
+get_text_relocs(Elf) ->
+ %% Only care about the symbol table index and the offset:
+ NameOffsetTemp = [{?ELF_R_SYM(r_info(E)), r_offset(E)}
+ || E <- extract_rela(Elf, ?TEXT)],
+ {NameIndices, ActualOffsets} = lists:unzip(NameOffsetTemp),
+ %% Find the names of the symbols:
+ %%
+ %% Get those symbol table entries that are related to Text relocs:
+ Symtab = extract_symtab(Elf),
+ SymtabEs = [get_symtab_entry(Symtab, Index) || Index <- NameIndices],
+ %XXX: not zero-indexed!
+ %% Symbol table entries contain the offset of the name of the symbol in
+ %% String Table:
+ SymtabEs2 = [sym_name(E) || E <- SymtabEs], %XXX: Do we need to sort SymtabE?
+ %% Get string table entries ([{Name, Offset in strtab section}]). Keep only
+ %% relevant entries:
+ Strtab = extract_strtab(Elf),
+ Relevant = [get_strtab_entry(Strtab, Off) || Off <- SymtabEs2],
+ %% Zip back with actual offsets:
+ lists:zip(Relevant, ActualOffsets).
+
+%% @doc Extract the Relocations segment for section `Name' (that is passed
+%% as second argument) from an ELF formated Object file binary.
+-spec extract_rela(elf(), name()) -> [elf_rel() | elf_rela()].
+extract_rela(Elf, Name) ->
+ SegName =
+ case is64bit() of
+ true -> ?RELA(Name); % ELF-64 uses ".rela"
+ false -> ?REL(Name) % ...while ELF-32 uses ".rel"
+ end,
+ Rela_bin = extract_segment_by_name(Elf, SegName),
+ get_rela_entries(Rela_bin, []).
+
+get_rela_entries(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_rela_entries(Bin, Acc) ->
+ E = case is64bit() of
+ true ->
+ <<%% Structural pattern matching on fields of a Rela Entry.
+ Offset:?bits(?R_OFFSET_SIZE)/integer-little,
+ Info:?bits(?R_INFO_SIZE)/integer-little,
+ Addend:?bits(?R_ADDEND_SIZE)/integer-little,
+ Rest/binary
+ >> = Bin,
+ mk_rela(Offset, Info, Addend);
+ false ->
+ <<%% Structural pattern matching on fields of a Rel Entry.
+ Offset:?bits(?R_OFFSET_SIZE)/integer-little,
+ Info:?bits(?R_INFO_SIZE)/integer-little,
+ Rest/binary
+ >> = Bin,
+ mk_rel(Offset, Info)
+ end,
+ get_rela_entries(Rest, [E | Acc]).
+
+%% %% @doc Extract the `EntryNum' (serial number) Relocation Entry.
+%% get_rela_entry(Rela, EntryNum) ->
+%% lists:nth(EntryNum + 1, Rela).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Executable Code segment
+%%------------------------------------------------------------------------------
+
+%% @doc This function gets as arguments an ELF formated binary file and
+%% returns the Executable Code (".text" segment) or an empty binary if it
+%% is not found.
+-spec extract_text(elf()) -> binary().
+extract_text(Elf) ->
+ extract_segment_by_name(Elf, ?TEXT).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Note Section
+%%------------------------------------------------------------------------------
+
+%% @doc Extract specific Note Section from an ELF Object file. The function
+%% takes as first argument the object file (`Elf') and the `Name' of the
+%% wanted Note Section (<b>without</b> the ".note" prefix!). It returns
+%% the specified binary segment or an empty binary if no such section
+%% exists.
+-spec extract_note(elf(), string()) -> binary().
+extract_note(Elf, Name) ->
+ extract_segment_by_name(Elf, ?NOTE(Name)).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate GCC Exception Table segment
+%%------------------------------------------------------------------------------
+
+%% A description for the C++ exception table formats can be found at Exception
+%% Handling Tables (http://www.codesourcery.com/cxx-abi/exceptions.pdf).
+
+%% A list with `{Start, End, HandlerOffset}' for all call sites in the code
+-spec get_exn_handlers(elf()) -> [{start(), start(), lp()}].
+get_exn_handlers(Elf) ->
+ CallSites = extract_gccexntab_callsites(Elf),
+ [{Start, Start + Size, LP}
+ || #elf_gccexntab_callsite{start = Start, size = Size, lp = LP} <- CallSites].
+
+%% @doc This function gets as argument an ELF binary file and returns
+%% the table (list) of call sites which is stored in GCC
+%% Exception Table (".gcc_except_table") section.
+%% It returns an empty list if the Exception Table is not found.
+%% XXX: Assumes there is *no* Action Record Table.
+extract_gccexntab_callsites(Elf) ->
+ case extract_segment_by_name(Elf, ?GCC_EXN_TAB) of
+ <<>> ->
+ [];
+ ExnTab ->
+ %% First byte of LSDA is Landing Pad base encoding.
+ <<LBenc:8, More/binary>> = ExnTab,
+ %% Second byte is the Landing Pad base (if its encoding is not
+ %% DW_EH_PE_omit) (optional).
+ {_LPBase, LSDACont} =
+ case LBenc =:= ?DW_EH_PE_omit of
+ true -> % No landing pad base byte. (-1 denotes that)
+ {-1, More};
+ false -> % Landing pad base.
+ <<Base:8, More2/binary>> = More,
+ {Base, More2}
+ end,
+ %% Next byte of LSDA is the encoding of the Type Table.
+ <<TTenc:8, More3/binary>> = LSDACont,
+ %% Next byte is the Types Table offset encoded in U-LEB128 (optional).
+ {_TTOff, LSDACont2} =
+ case TTenc =:= ?DW_EH_PE_omit of
+ true -> % There is no Types Table pointer. (-1 denotes that)
+ {-1, More3};
+ false -> % The byte offset from this field to the start of the Types
+ % Table used for exception matching.
+ leb128_decode(More3)
+ end,
+ %% Next byte of LSDA is the encoding of the fields in the Call-site Table.
+ <<_CSenc:8, More4/binary>> = LSDACont2,
+ %% Sixth byte is the size (in bytes) of the Call-site Table encoded in
+ %% U-LEB128.
+ {_CSTabSize, CSTab} = leb128_decode(More4),
+ %% Extract all call site information
+ get_gccexntab_callsites(CSTab, [])
+ end.
+
+get_gccexntab_callsites(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_gccexntab_callsites(CSTab, Acc) ->
+ %% We are only interested in the Landing Pad of every entry.
+ <<Start:32/integer-little, Size:32/integer-little,
+ LP:32/integer-little, OnAction:8, More/binary
+ >> = CSTab,
+ GccCS = mk_gccexntab_callsite(Start, Size, LP, OnAction),
+ get_gccexntab_callsites(More, [GccCS | Acc]).
+
+%%------------------------------------------------------------------------------
+%% Functions to manipulate Read-only Data (.rodata)
+%%------------------------------------------------------------------------------
+extract_rodata(Elf) ->
+ Rodata_bin = extract_segment_by_name(Elf, ?RODATA),
+ get_rodata_entries(Rodata_bin, []).
+
+get_rodata_entries(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_rodata_entries(Rodata_bin, Acc) ->
+ <<Num:?bits(?ELF_ADDR_SIZE)/integer-little, More/binary>> = Rodata_bin,
+ get_rodata_entries(More, [Num | Acc]).
+
+%%------------------------------------------------------------------------------
+%% Helper functions
+%%------------------------------------------------------------------------------
+
+%% @doc Returns the binary segment starting at `Offset' with length `Size'
+%% (bytes) from a binary file. If `Offset' is bigger than the byte size of
+%% the binary, an empty binary (`<<>>') is returned.
+-spec get_binary_segment(binary(), offset(), size()) -> binary().
+get_binary_segment(Bin, Offset, _Size) when Offset > byte_size(Bin) ->
+ <<>>;
+get_binary_segment(Bin, Offset, Size) ->
+ <<_Hdr:Offset/binary, BinSeg:Size/binary, _More/binary>> = Bin,
+ BinSeg.
+
+%% @doc This function gets as arguments an ELF formated binary object and
+%% a string with the segments' name and returns the specified segment or
+%% an empty binary (`<<>>') if there exists no segment with that name.
+%% There are handy macros defined in elf_format.hrl for all Standard
+%% Section Names.
+-spec extract_segment_by_name(elf(), string()) -> binary().
+extract_segment_by_name(Elf, SectionName) ->
+ %% Extract Section Header Table and Section Header String Table from binary
+ SHdrTable = extract_shdrtab(Elf),
+ Names = extract_shstrtab(Elf),
+ %% Zip to a list of (Name,ShdrE)
+ [_Zero | ShdrEs] = lists:keysort(2, SHdrTable), % Skip first entry (zeros).
+ L = lists:zip(Names, ShdrEs),
+ %% Find Section Header Table entry by name
+ case lists:keyfind(SectionName, 1, L) of
+ {SectionName, ShdrE} -> %% Note: Same name.
+ #elf_shdr{offset = Offset, size = Size} = ShdrE,
+ get_binary_segment(Elf, Offset, Size);
+ false -> %% Not found.
+ <<>>
+ end.
+
+%% @doc Extracts a list of strings with (zero-separated) names from a binary.
+%% Returns tuples of `{Name, Size}'.
+%% XXX: Skip trailing 0.
+-spec get_names(<<_:8,_:_*8>>) -> name_sizes().
+get_names(<<0, Bin/binary>>) ->
+ NamesSizes = get_names(Bin, []),
+ fix_names(NamesSizes, []).
+
+get_names(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_names(Bin, Acc) ->
+ {Name, MoreNames} = bin_get_string(Bin),
+ get_names(MoreNames, [{Name, length(Name)} | Acc]).
+
+%% @doc Fix names:
+%% e.g. If ".rela.text" exists, ".text" does not. Same goes for
+%% ".rel.text". In that way, the Section Header String Table is more
+%% compact. Add ".text" just *before* the corresponding rela-field,
+%% etc.
+-spec fix_names(name_sizes(), name_sizes()) -> name_sizes().
+fix_names([], Acc) ->
+ lists:reverse(Acc);
+fix_names([{Name, Size}=T | Names], Acc) ->
+ case is64bit() of
+ true ->
+ case string:str(Name, ".rela") =:= 1 of
+ true -> %% Name starts with ".rela":
+ Section = string:substr(Name, 6),
+ fix_names(Names, [{Section, Size - 5}
+ | [T | Acc]]); % XXX: Is order ok? (".text"
+ % always before ".rela.text")
+ false -> %% Name does not start with ".rela":
+ fix_names(Names, [T | Acc])
+ end;
+ false ->
+ case string:str(Name, ".rel") =:= 1 of
+ true -> %% Name starts with ".rel":
+ Section = string:substr(Name, 5),
+ fix_names(Names, [{Section, Size - 4}
+ | [T | Acc]]); % XXX: Is order ok? (".text"
+ % always before ".rela.text")
+ false -> %% Name does not start with ".rel":
+ fix_names(Names, [T | Acc])
+ end
+ end.
+
+
+%% @doc A function that byte-reverses a binary. This might be needed because of
+%% little (fucking!) endianess.
+-spec bin_reverse(binary()) -> binary().
+bin_reverse(Bin) when is_binary(Bin) ->
+ bin_reverse(Bin, <<>>).
+
+-spec bin_reverse(binary(), binary()) -> binary().
+bin_reverse(<<>>, Acc) ->
+ Acc;
+bin_reverse(<<Head, More/binary>>, Acc) ->
+ bin_reverse(More, <<Head, Acc/binary>>).
+
+%% @doc A function that extracts a null-terminated string from a binary. It
+%% returns the found string along with the rest of the binary.
+-spec bin_get_string(binary()) -> {string(), binary()}.
+bin_get_string(Bin) ->
+ bin_get_string(Bin, <<>>).
+
+bin_get_string(<<>>, BinAcc) ->
+ Bin = bin_reverse(BinAcc), % little endian!
+ {binary_to_list(Bin), <<>>};
+bin_get_string(<<0, MoreBin/binary>>, BinAcc) ->
+ Bin = bin_reverse(BinAcc), % little endian!
+ {binary_to_list(Bin), MoreBin};
+bin_get_string(<<Letter, Tail/binary>>, BinAcc) ->
+ bin_get_string(Tail, <<Letter, BinAcc/binary>>).
+
+%% @doc
+make_offsets(NamesSizes) ->
+ {Names, Sizes} = lists:unzip(NamesSizes),
+ Offsets = make_offsets_from_sizes(Sizes, 1, []),
+ lists:zip(Names, Offsets).
+
+make_offsets_from_sizes([], _, Acc) ->
+ lists:reverse(Acc);
+make_offsets_from_sizes([Size | Sizes], Cur, Acc) ->
+ make_offsets_from_sizes(Sizes, Size+Cur+1, [Cur | Acc]). % For the "."!
+
+%% @doc Little-Endian Base 128 (LEB128) Decoder
+%% This function extracts the <b>first</b> LEB128-encoded integer in a
+%% binary and returns that integer along with the remaining binary. This is
+%% done because a LEB128 number has variable bit-size and that is a way of
+%% extracting only one number in a binary and continuing parsing the binary
+%% for other kind of data (e.g. different encoding).
+%% FIXME: Only decodes unsigned data!
+-spec leb128_decode(binary()) -> {integer(), binary()}.
+leb128_decode(LebNum) ->
+ leb128_decode(LebNum, 0, <<>>).
+
+-spec leb128_decode(binary(), integer(), binary()) -> {integer(), binary()}.
+leb128_decode(LebNum, NoOfBits, Acc) ->
+ <<Sentinel:1/bits, NextBundle:7/bits, MoreLebNums/bits>> = LebNum,
+ case Sentinel of
+ <<1:1>> -> % more bytes to follow
+ leb128_decode(MoreLebNums, NoOfBits+7, <<NextBundle:7/bits, Acc/bits>>);
+ <<0:1>> -> % byte bundle stop
+ Size = NoOfBits+7,
+ <<Num:Size/integer>> = <<NextBundle:7/bits, Acc/bits>>,
+ {Num, MoreLebNums}
+ end.
+
+%% @doc Extract ELF Class from ELF header and export symbol to process
+%% dictionary.
+-spec set_architecture_flag(elf()) -> 'ok'.
+set_architecture_flag(Elf) ->
+ %% Extract information about ELF Class from ELF Header
+ <<16#7f, $E, $L, $F, EI_Class, _MoreHeader/binary>>
+ = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE),
+ put(elf_class, EI_Class),
+ ok.
+
+%% @doc Read from object file header if the file class is ELF32 or ELF64.
+-spec is64bit() -> boolean().
+is64bit() ->
+ case get(elf_class) of
+ ?ELFCLASS64 -> true;
+ ?ELFCLASS32 -> false
+ end.
diff --git a/lib/hipe/llvm/elf_format.hrl b/lib/hipe/llvm/elf_format.hrl
new file mode 100644
index 0000000000..78592e6e2a
--- /dev/null
+++ b/lib/hipe/llvm/elf_format.hrl
@@ -0,0 +1,488 @@
+%% -*- erlang-indent-level: 2 -*-
+
+%%% @copyright 2011-2014 Yiannis Tsiouris <[email protected]>,
+%%% Chris Stavrakakis <[email protected]>
+%%% @author Yiannis Tsiouris <[email protected]>
+%%% [http://www.softlab.ntua.gr/~gtsiour/]
+
+%%------------------------------------------------------------------------------
+%%
+%% ELF Header File
+%%
+%%------------------------------------------------------------------------------
+
+-ifdef(BIT32).
+-include("elf32_format.hrl"). % ELF32-specific definitions.
+-else.
+-include("elf64_format.hrl"). % ELF64-specific definitions.
+-endif.
+
+%%------------------------------------------------------------------------------
+%% ELF Data Types (in bytes)
+%%------------------------------------------------------------------------------
+%%XXX: Included in either elf32_format or elf64_format.
+
+%%------------------------------------------------------------------------------
+%% ELF File Header
+%%------------------------------------------------------------------------------
+-define(ELF_EHDR_SIZE, (?E_IDENT_SIZE + ?E_TYPE_SIZE + ?E_MACHINE_SIZE
+ +?E_VERSION_SIZE + ?E_ENTRY_SIZE + ?E_PHOFF_SIZE
+ +?E_SHOFF_SIZE + ?E_FLAGS_SIZE + ?E_EHSIZE_SIZE
+ +?E_PHENTSIZE_SIZE + ?E_PHNUM_SIZE + ?E_SHENTSIZE_SIZE
+ +?E_SHNUM_SIZE + ?E_SHSTRNDX_SIZE) ).
+
+-define(E_IDENT_SIZE, (16 * ?ELF_UNSIGNED_CHAR_SIZE) ).
+-define(E_TYPE_SIZE, ?ELF_HALF_SIZE).
+-define(E_MACHINE_SIZE, ?ELF_HALF_SIZE).
+-define(E_VERSION_SIZE, ?ELF_WORD_SIZE).
+-define(E_ENTRY_SIZE, ?ELF_ADDR_SIZE).
+-define(E_PHOFF_SIZE, ?ELF_OFF_SIZE).
+-define(E_SHOFF_SIZE, ?ELF_OFF_SIZE).
+-define(E_FLAGS_SIZE, ?ELF_WORD_SIZE).
+-define(E_EHSIZE_SIZE, ?ELF_HALF_SIZE).
+-define(E_PHENTSIZE_SIZE, ?ELF_HALF_SIZE).
+-define(E_PHNUM_SIZE, ?ELF_HALF_SIZE).
+-define(E_SHENTSIZE_SIZE, ?ELF_HALF_SIZE).
+-define(E_SHNUM_SIZE, ?ELF_HALF_SIZE).
+-define(E_SHSTRNDX_SIZE, ?ELF_HALF_SIZE).
+
+%% Useful arithmetics for computing byte offsets for various File Header
+%% entries from a File Header (erlang) binary
+-define(E_IDENT_OFFSET, 0).
+-define(E_TYPE_OFFSET, (?E_IDENT_OFFSET + ?E_IDENT_SIZE) ).
+-define(E_MACHINE_OFFSET, (?E_TYPE_OFFSET + ?E_TYPE_SIZE) ).
+-define(E_VERSION_OFFSET, (?E_MACHINE_OFFSET + ?E_MACHINE_SIZE) ).
+-define(E_ENTRY_OFFSET, (?E_VERSION_OFFSET + ?E_VERSION_SIZE) ).
+-define(E_PHOFF_OFFSET, (?E_ENTRY_OFFSET + ?E_ENTRY_SIZE) ).
+-define(E_SHOFF_OFFSET, (?E_PHOFF_OFFSET + ?E_PHOFF_SIZE) ).
+-define(E_FLAGS_OFFSET, (?E_SHOFF_OFFSET + ?E_SHOFF_SIZE) ).
+-define(E_EHSIZE_OFFSET, (?E_FLAGS_OFFSET + ?E_FLAGS_SIZE) ).
+-define(E_PHENTSIZE_OFFSET, (?E_EHSIZE_OFFSET + ?E_EHSIZE_SIZE) ).
+-define(E_PHNUM_OFFSET, (?E_PHENTSIZE_OFFSET + ?E_PHENTSIZE_SIZE) ).
+-define(E_SHENTSIZE_OFFSET, (?E_PHNUM_OFFSET + ?E_PHNUM_SIZE) ).
+-define(E_SHNUM_OFFSET, (?E_SHENTSIZE_OFFSET + ?E_SHENTSIZE_SIZE) ).
+-define(E_SHSTRNDX_OFFSET, (?E_SHNUM_OFFSET + ?E_SHNUM_SIZE) ).
+
+%% Name aliases of File Header fields information used in get_header_field
+%% function of elf64_format module.
+-define(E_IDENT, {?E_IDENT_OFFSET, ?E_IDENT_SIZE}).
+-define(E_TYPE, {?E_TYPE_OFFSET, ?E_TYPE_SIZE}).
+-define(E_MACHINE, {?E_MACHINE_OFFSET, ?E_MACHINE_SIZE}).
+-define(E_VERSION, {?E_VERSION_OFFSET, ?E_VERSION_SIZE})
+-define(E_ENTRY, {?E_ENTRY_OFFSET, ?E_ENTRY_SIZE}).
+-define(E_PHOFF, {?E_PHOFF_OFFSET, ?E_PHOFF_SIZE}).
+-define(E_SHOFF, {?E_SHOFF_OFFSET, ?E_SHOFF_SIZE}).
+-define(E_FLAGS, {?E_FLAGS_OFFSET, ?E_FLAGS_SIZE}).
+-define(E_EHSIZE, {?E_EHSIZE_OFFSET, ?E_EHSIZE_SIZE}).
+-define(E_PHENTSIZE, {?E_PHENTSIZE_OFFSET, ?E_PHENTSIZE_SIZE}).
+-define(E_PHNUM, {?E_PHNUM_OFFSET, ?E_PHNUM_SIZE}).
+-define(E_SHENTSIZE, {?E_SHENTSIZE_OFFSET, ?E_SHENTSIZE_SIZE}).
+-define(E_SHNUM, {?E_SHNUM_OFFSET, ?E_SHNUM_SIZE}).
+-define(E_SHSTRNDX, {?E_SHSTRNDX_OFFSET, ?E_SHSTRNDX_SIZE}).
+
+%% ELF Identification (e_ident)
+-define(EI_MAG0, 0).
+-define(EI_MAG1, 1).
+-define(EI_MAG2, 2).
+-define(EI_MAG3, 3).
+-define(EI_CLASS, 4).
+-define(EI_DATA, 5).
+-define(EI_VERSION, 6).
+-define(EI_OSABI, 7).
+-define(EI_ABIVERSION, 8).
+-define(EI_PAD, 9).
+-define(EI_NIDENT, 16).
+
+%% Object File Classes (e_ident[EI_CLASS])
+-define(ELFCLASSNONE, 0).
+-define(ELFCLASS32, 1).
+-define(ELFCLASS64, 2).
+
+%% Data Encodings (e_ident[EI_DATA])
+-define(ELFDATA2LSB, 1).
+-define(ELFDATA2MSB, 2).
+
+%% Operating System and ABI Identifiers (e_ident[EI_OSABI])
+-define(ELFOSABI_SYSV, 0).
+-define(ELFOSABI_HPUX, 1).
+-define(ELFOSABI_STANDALONE, 255).
+
+%% Object File Types (e_type)
+-define(ET_NONE, 0).
+-define(ET_REL, 1).
+-define(ET_EXEC, 2).
+-define(ET_DYN, 3).
+-define(ET_CORE, 4).
+-define(ET_LOOS, 16#FE00).
+-define(ET_HIOS, 16#FEFF).
+-define(ET_LOPROC, 16#FF00).
+-define(ET_HIPROC, 16#FFFF).
+
+%%------------------------------------------------------------------------------
+%% ELF Section Header
+%%------------------------------------------------------------------------------
+-define(ELF_SHDRENTRY_SIZE, (?SH_NAME_SIZE + ?SH_TYPE_SIZE + ?SH_FLAGS_SIZE
+ +?SH_ADDR_SIZE + ?SH_OFFSET_SIZE + ?SH_SIZE_SIZE
+ +?SH_LINK_SIZE + ?SH_INFO_SIZE
+ +?SH_ADDRALIGN_SIZE + ?SH_ENTSIZE_SIZE) ).
+
+-define(SH_NAME_SIZE, ?ELF_WORD_SIZE).
+-define(SH_TYPE_SIZE, ?ELF_WORD_SIZE).
+-define(SH_FLAGS_SIZE, ?ELF_XWORD_SIZE).
+-define(SH_ADDR_SIZE, ?ELF_ADDR_SIZE).
+-define(SH_OFFSET_SIZE, ?ELF_OFF_SIZE).
+-define(SH_SIZE_SIZE, ?ELF_XWORD_SIZE).
+-define(SH_LINK_SIZE, ?ELF_WORD_SIZE).
+-define(SH_INFO_SIZE, ?ELF_WORD_SIZE).
+-define(SH_ADDRALIGN_SIZE, ?ELF_XWORD_SIZE).
+-define(SH_ENTSIZE_SIZE, ?ELF_XWORD_SIZE).
+
+%% Useful arithmetics for computing byte offsets for various fields from a
+%% Section Header Entry (erlang) binary
+-define(SH_NAME_OFFSET, 0).
+-define(SH_TYPE_OFFSET, (?SH_NAME_OFFSET + ?SH_NAME_SIZE) ).
+-define(SH_FLAGS_OFFSET, (?SH_TYPE_OFFSET + ?SH_TYPE_SIZE) ).
+-define(SH_ADDR_OFFSET, (?SH_FLAGS_OFFSET + ?SH_FLAGS_SIZE) ).
+-define(SH_OFFSET_OFFSET, (?SH_ADDR_OFFSET + ?SH_ADDR_SIZE) ).
+-define(SH_SIZE_OFFSET, (?SH_OFFSET_OFFSET + ?SH_OFFSET_SIZE) ).
+-define(SH_LINK_OFFSET, (?SH_SIZE_OFFSET + ?SH_SIZE_SIZE) ).
+-define(SH_INFO_OFFSET, (?SH_LINK_OFFSET + ?SH_LINK_SIZE) ).
+-define(SH_ADDRALIGN_OFFSET, (?SH_INFO_OFFSET + ?SH_INFO_SIZE) ).
+-define(SH_ENTSIZE_OFFSET, (?SH_ADDRALIGN_OFFSET + ?SH_ADDRALIGN_SIZE) ).
+
+%% Name aliases of Section Header Table entry information used in
+%% get_shdrtab_entry function of elf64_format module.
+-define(SH_NAME, {?SH_NAME_OFFSET, ?SH_NAME_SIZE}).
+-define(SH_TYPE, {?SH_TYPE_OFFSET, ?SH_TYPE_SIZE}).
+-define(SH_FLAGS, {?SH_FLAGS_OFFSET, ?SH_FLAGS_SIZE}).
+-define(SH_ADDR, {?SH_ADDR_OFFSET, ?SH_ADDR_SIZE}).
+-define(SH_OFFSET, {?SH_OFFSET_OFFSET, ?SH_OFFSET_SIZE}).
+-define(SH_SIZE, {?SH_SIZE_OFFSET, ?SH_SIZE_SIZE}).
+-define(SH_LINK, {?SH_LINK_OFFSET, ?SH_LINK_SIZE}).
+-define(SH_INFO, {?SH_INFO_OFFSET, ?SH_INFO_SIZE}).
+-define(SH_ADDRALIGN, {?SH_ADDRALIGN_OFFSET, ?SH_ADDRALIGN_SIZE}).
+-define(SH_ENTSIZE, {?SH_ENTSIZE_OFFSET, ?SH_ENTSIZE_SIZE}).
+
+%% Section Indices
+-define(SHN_UNDEF, 0).
+-define(SHN_LOPROC, 16#FF00).
+-define(SHN_HIPROC, 16#FF1F).
+-define(SHN_LOOS, 16#FF20).
+-define(SHN_HIOS, 16#FF3F).
+-define(SHN_ABS, 16#FFF1).
+-define(SHN_COMMON, 16#FFF2).
+
+%% Section Types (sh_type)
+-define(SHT_NULL, 0).
+-define(SHT_PROGBITS, 1).
+-define(SHT_SYMTAB, 2).
+-define(SHT_STRTAB, 3).
+-define(SHT_RELA, 4).
+-define(SHT_HASH, 5).
+-define(SHT_DYNAMIC, 6).
+-define(SHT_NOTE, 7).
+-define(SHT_NOBITS, 8).
+-define(SHT_REL, 9).
+-define(SHT_SHLIB, 10).
+-define(SHT_DYNSYM, 11).
+-define(SHT_LOOS, 16#60000000).
+-define(SHT_HIOS, 16#6FFFFFFF).
+-define(SHT_LOPROC, 16#70000000).
+-define(SHT_HIPROC, 16#7FFFFFFF).
+
+%% Section Attributes (sh_flags)
+-define(SHF_WRITE, 16#1).
+-define(SHF_ALLOC, 16#2).
+-define(SHF_EXECINSTR, 16#4).
+-define(SHF_MASKOS, 16#0F000000).
+-define(SHF_MASKPROC, 16#F0000000).
+
+%%
+%% Standard Section names for Code and Data
+%%
+-define(BSS, ".bss").
+-define(DATA, ".data").
+-define(INTERP, ".interp").
+-define(RODATA, ".rodata").
+-define(TEXT, ".text").
+%% Other Standard Section names
+-define(COMMENT, ".comment").
+-define(DYNAMIC, ".dynamic").
+-define(DYNSTR, ".dynstr").
+-define(GOT, ".got").
+-define(HASH, ".hash").
+-define(NOTE(Name), (".note" ++ Name)).
+-define(PLT, ".plt").
+-define(REL(Name), (".rel" ++ Name) ).
+-define(RELA(Name), (".rela" ++ Name) ).
+-define(SHSTRTAB, ".shstrtab").
+-define(STRTAB, ".strtab").
+-define(SYMTAB, ".symtab").
+-define(GCC_EXN_TAB, ".gcc_except_table").
+
+%%------------------------------------------------------------------------------
+%% ELF Symbol Table Entries
+%%------------------------------------------------------------------------------
+-define(ELF_SYM_SIZE, (?ST_NAME_SIZE + ?ST_INFO_SIZE + ?ST_OTHER_SIZE
+ +?ST_SHNDX_SIZE + ?ST_VALUE_SIZE + ?ST_SIZE_SIZE) ).
+
+-define(ST_NAME_SIZE, ?ELF_WORD_SIZE).
+-define(ST_INFO_SIZE, ?ELF_UNSIGNED_CHAR_SIZE).
+-define(ST_OTHER_SIZE, ?ELF_UNSIGNED_CHAR_SIZE).
+-define(ST_SHNDX_SIZE, ?ELF_HALF_SIZE).
+-define(ST_VALUE_SIZE, ?ELF_ADDR_SIZE).
+-define(ST_SIZE_SIZE, ?ELF_XWORD_SIZE).
+
+%% Precomputed offset for Symbol Table entries in SymTab binary
+%%XXX: Included in either elf32_format or elf64_format.
+
+%% Name aliases for Symbol Table entry information
+-define(ST_NAME, {?ST_NAME_OFFSET, ?ST_NAME_SIZE}).
+-define(ST_INFO, {?ST_INFO_OFFSET, ?ST_INFO_SIZE}).
+-define(ST_OTHER, {?ST_OTHER_OFFSET, ?ST_OTHER_SIZE}).
+-define(ST_SHNDX, {?ST_SHNDX_OFFSET, ?ST_SHNDX_SIZE}).
+-define(ST_VALUE, {?ST_VALUE_OFFSET, ?ST_VALUE_SIZE}).
+-define(ST_SIZE, {?ST_SIZE_OFFSET, ?ST_SIZE_SIZE}).
+
+%% Macros to extract information from st_type
+-define(ELF_ST_BIND(I), (I bsr 4) ).
+-define(ELF_ST_TYPE(I), (I band 16#f) ).
+-define(ELF_ST_INFO(B,T), (B bsl 4 + T band 16#f) ).
+
+%% Symbol Bindings
+-define(STB_LOCAL, 0).
+-define(STB_GLOBAL, 1).
+-define(STB_WEAK, 2).
+-define(STB_LOOS, 10).
+-define(STB_HIOS, 12).
+-define(STB_LOPROC, 13).
+-define(STB_HIPROC, 15).
+
+%% Symbol Types
+-define(STT_NOTYPE, 0).
+-define(STT_OBJECT, 1).
+-define(STT_FUNC, 2).
+-define(STT_SECTION, 3).
+-define(STT_FILE, 4).
+-define(STT_LOOS, 10).
+-define(STT_HIOS, 12).
+-define(STT_LOPROC, 13).
+-define(STT_HIPROC, 15).
+
+%%------------------------------------------------------------------------------
+%% ELF Relocation Entries
+%%------------------------------------------------------------------------------
+-define(ELF_REL_SIZE, (?R_OFFSET_SIZE + ?R_INFO_SIZE) ).
+-define(ELF_RELA_SIZE, (?R_OFFSET_SIZE + ?R_INFO_SIZE + ?R_ADDEND_SIZE) ).
+
+-define(R_OFFSET_SIZE, ?ELF_ADDR_SIZE).
+-define(R_INFO_SIZE, ?ELF_XWORD_SIZE).
+-define(R_ADDEND_SIZE, ?ELF_SXWORD_SIZE).
+
+%% Arithmetics for computing byte offsets in a Relocation entry binary
+-define(R_OFFSET_OFFSET, 0).
+-define(R_INFO_OFFSET, (?R_OFFSET_OFFSET + ?R_OFFSET_SIZE) ).
+-define(R_ADDEND_OFFSET, (?R_INFO_OFFSET + ?R_INFO_SIZE) ).
+
+%% Name aliases for Relocation field information
+-define(R_OFFSET, {?R_OFFSET_OFFSET, ?R_OFFSET_SIZE}).
+-define(R_INFO, {?R_INFO_OFFSET, ?R_INFO_SIZE}).
+-define(R_ADDEND, {?R_ADDEND_OFFSET, ?R_ADDEND_SIZE}).
+
+%% Useful macros to extract information from r_info field
+%%XXX: Included in either elf32_format or elf64_format.
+
+%%------------------------------------------------------------------------------
+%% ELF Program Header Table
+%%------------------------------------------------------------------------------
+-define(ELF_PHDR_SIZE, (?P_TYPE_SIZE + ?P_FLAGS_SIZE + ?P_OFFSET_SIZE
+ +?P_VADDR_SIZE + ?P_PADDR_SIZE + ?P_FILESZ_SIZE
+ +?P_MEMSZ_SIZE + ?P_ALIGN_SIZE) ).
+
+-define(P_TYPE_SIZE, ?ELF_WORD_SIZE).
+-define(P_FLAGS_SIZE, ?ELF_WORD_SIZE).
+-define(P_OFFSET_SIZE, ?ELF_OFF_SIZE).
+-define(P_VADDR_SIZE, ?ELF_ADDR_SIZE).
+-define(P_PADDR_SIZE, ?ELF_ADDR_SIZE).
+-define(P_FILESZ_SIZE, ?ELF_XWORD_SIZE).
+-define(P_MEMSZ_SIZE, ?ELF_XWORD_SIZE).
+-define(P_ALIGN_SIZE, ?ELF_XWORD_SIZE).
+
+%% Offsets of various fields in a Program Header Table entry binary.
+%%XXX: Included in either elf32_format or elf64_format.
+
+%% Name aliases for each Program Header Table entry field information.
+-define(P_TYPE, {?P_TYPE_OFFSET, ?P_TYPE_SIZE} ).
+-define(P_FLAGS, {?P_FLAGS_OFFSET, ?P_FLAGS_SIZE} ).
+-define(P_OFFSET, {?P_OFFSET_OFFSET, ?P_OFFSET_SIZE} ).
+-define(P_VADDR, {?P_VADDR_OFFSET, ?P_VADDR_SIZE} ).
+-define(P_PADDR, {?P_PADDR_OFFSET, ?P_PADDR_SIZE} ).
+-define(P_FILESZ, {?P_FILESZ_OFFSET, ?P_FILESZ_SIZE} ).
+-define(P_MEMSZ, {?P_MEMSZ_OFFSET, ?P_MEMSZ_SIZE} ).
+-define(P_ALIGN, {?P_ALIGN_OFFSET, ?P_ALIGN_SIZE} ).
+
+%% Segment Types (p_type)
+-define(PT_NULL, 0).
+-define(PT_LOAD, 1).
+-define(PT_DYNAMIC, 2).
+-define(PT_INTERP, 3).
+-define(PT_NOTE, 4).
+-define(PT_SHLIB, 5).
+-define(PT_PHDR, 6).
+-define(PT_LOOS, 16#60000000).
+-define(PT_HIOS, 16#6FFFFFFF).
+-define(PT_LOPROC, 16#70000000).
+-define(PT_HIPROC, 16#7FFFFFFF).
+
+%% Segment Attributes (p_flags)
+-define(PF_X, 16#1).
+-define(PF_W, 16#2).
+-define(PF_R, 16#4).
+-define(PF_MASKOS, 16#00FF0000).
+-define(PF_MASKPROC, 16#FF000000).
+
+%%------------------------------------------------------------------------------
+%% ELF Dynamic Table
+%%------------------------------------------------------------------------------
+-define(ELF_DYN_SIZE, (?D_TAG_SIZE + ?D_VAL_PTR_SIZE) ).
+
+-define(D_TAG_SIZE, ?ELF_SXWORD_SIZE).
+-define(D_VAL_PTR_SIZE, ?ELF_ADDR_SIZE).
+
+%% Offsets of each field in Dynamic Table entry in binary
+-define(D_TAG_OFFSET, 0).
+-define(D_VAL_PTR_OFFSET, (?D_TAG_OFFSET + ?D_TAG_SIZE)).
+
+%% Name aliases for each field of a Dynamic Table entry information
+-define(D_TAG, {?D_TAG_OFFSET, ?D_TAG_SIZE} ).
+-define(D_VAL_PTR, {?D_VAL_PTR_OFFSET, ?D_VAL_PTR_SIZE} ).
+
+%% Dynamic Table Entries
+-define(DT_NULL, 0).
+-define(DT_NEEDED, 1).
+-define(DT_PLTRELSZ, 2).
+-define(DT_PLTGOT, 3).
+-define(DT_HASH, 4).
+-define(DT_STRTAB, 5).
+-define(DT_SYMTAB, 6).
+-define(DT_RELA, 7).
+-define(DT_RELASZ, 8).
+-define(DT_RELAENT, 9).
+-define(DT_STRSZ, 10).
+-define(DT_SYMENT, 11).
+-define(DT_INIT, 12).
+-define(DT_FINI, 13).
+-define(DT_SONAME, 14).
+-define(DT_RPATH, 15).
+-define(DT_SYMBOLIC, 16).
+-define(DT_REL, 17).
+-define(DT_RELSZ, 18).
+-define(DT_RELENT, 19).
+-define(DT_PLTREL, 20).
+-define(DT_DEBUG, 21).
+-define(DT_TEXTREL, 22).
+-define(DT_JMPREL, 23).
+-define(DT_BIND_NOW, 24).
+-define(DT_INIT_ARRAY, 25).
+-define(DT_FINI_ARRAY, 26).
+-define(DT_INIT_ARRAYSZ, 27).
+-define(DT_FINI_ARRAYSZ, 28).
+-define(DT_LOOS, 16#60000000).
+-define(DT_HIOS, 16#6FFFFFFF).
+-define(DT_LOPROC, 16#700000000).
+-define(DT_HIPROC, 16#7FFFFFFFF).
+
+%%------------------------------------------------------------------------------
+%% ELF GCC Exception Table
+%%------------------------------------------------------------------------------
+
+%% The DWARF Exception Header Encoding is used to describe the type of data used
+%% in the .eh_frame_hdr (and .gcc_except_table) section. The upper 4 bits
+%% indicate how the value is to be applied. The lower 4 bits indicate the format
+%% of the data.
+
+%% DWARF Exception Header value format
+-define(DW_EH_PE_omit, 16#ff). % No value is present.
+-define(DW_EH_PE_uleb128, 16#01). % Unsigned value encoded using LEB128.
+-define(DW_EH_PE_udata2, 16#02). % A 2 bytes unsigned value.
+-define(DW_EH_PE_udata4, 16#03). % A 4 bytes unsigned value.
+-define(DW_EH_PE_udata8, 16#04). % An 8 bytes unsigned value.
+-define(DW_EH_PE_sleb128, 16#09). % Signed value encoded using LEB128.
+-define(DW_EH_PE_sdata2, 16#0a). % A 2 bytes signed value.
+-define(DW_EH_PE_sdata4, 16#0b). % A 4 bytes signed value.
+-define(DW_EH_PE_sdata8, 16#0c). % An 8 bytes signed value.
+
+%% DWARF Exception Header application
+-define(DW_EH_PE_absptr, 16#00). % Value is used with no modification.
+-define(DW_EH_PE_pcrel, 16#10). % Value is relative to the current PC.
+-define(DW_EH_PE_datarel, 16#30). % Value is relative to the beginning of the
+ % section.
+
+%%------------------------------------------------------------------------------
+%% ELF Read-only data (constants, literlas etc.)
+%%------------------------------------------------------------------------------
+-define(RO_ENTRY_SIZE, 8).
+
+%%------------------------------------------------------------------------------
+%% Custom Note section: ".note.gc" for Erlang GC
+%%------------------------------------------------------------------------------
+
+%% The structure of this section is the following:
+%%
+%% .short <n> # number of safe points in code
+%%
+%% .long .L<label1> # safe point address |
+%% .long .L<label2> # safe point address |-> safe point addrs
+%% ..... |
+%% .long .L<label3> # safe point address |
+%%
+%% .short <n> # stack frame size (in words) |-> fixed-size part
+%% .short <n> # stack arity |
+%% .short <n> # number of live roots that follow |
+%%
+%% .short <n> # live root's stack index |
+%% ..... |-> live root indices
+%% .short <n> # >> |
+
+%% The name of the custom Note Section
+-define(NOTE_ERLGC_NAME, ".gc").
+
+%% The first word of a Note Section for Erlang GC (".note.gc") is always the
+%% number of safepoints in code.
+-define(SP_COUNT, {?SP_COUNT_OFFSET, ?SP_COUNT_SIZE}).
+-define(SP_COUNT_SIZE, ?ELF_HALF_SIZE).
+-define(SP_COUNT_OFFSET, 0). %(always the first entry in sdesc)
+
+%% The fixed-size part of a safe point (SP) entry consists of 4 words: the SP
+%% address (offset in code), the stack frame size of the function (where the SP
+%% is located), the stack arity of the function (the registered values are *not*
+%% counted), the number of live roots in the specific SP.
+-define(SP_FIXED, {?SP_FIXED_OFF, ?SP_FIXED_SIZE}).
+-define(SP_FIXED_OFF, 0).
+%%XXX: Exclude SP_ADDR_SIZE from SP_FIXED_SIZE in lew of new GC layout
+-define(SP_FIXED_SIZE, (?SP_STKFRAME_SIZE + ?SP_STKARITY_SIZE
+ + ?SP_LIVEROOTCNT_SIZE)).
+
+-define(SP_ADDR_SIZE, ?ELF_WORD_SIZE).
+-define(SP_STKFRAME_SIZE, ?ELF_HALF_SIZE).
+-define(SP_STKARITY_SIZE, ?ELF_HALF_SIZE).
+-define(SP_LIVEROOTCNT_SIZE, ?ELF_HALF_SIZE).
+
+%%XXX: SP_STKFRAME is the first piece of information in the new GC layout
+-define(SP_STKFRAME_OFFSET, 0).
+-define(SP_STKARITY_OFFSET, (?SP_STKFRAME_OFFSET + ?SP_STKFRAME_SIZE) ).
+-define(SP_LIVEROOTCNT_OFFSET, (?SP_STKARITY_OFFSET + ?SP_STKARITY_SIZE) ).
+
+%% Name aliases for safepoint fields.
+-define(SP_STKFRAME, {?SP_STKFRAME_OFFSET, ?SP_STKFRAME_SIZE}).
+-define(SP_STKARITY, {?SP_STKARITY_OFFSET, ?SP_STKARITY_SIZE}).
+-define(SP_LIVEROOTCNT, {?SP_LIVEROOTCNT_OFFSET, ?SP_LIVEROOTCNT_SIZE}).
+
+%% After the fixed-size part a variable-size part exists. This part holds the
+%% stack frame index of every live root in the specific SP.
+-define(LR_STKINDEX_SIZE, ?ELF_HALF_SIZE).
+
+%%------------------------------------------------------------------------------
+%% Misc.
+%%------------------------------------------------------------------------------
+-define(bits(Bytes), ((Bytes) bsl 3)).
diff --git a/lib/hipe/llvm/hipe_llvm.erl b/lib/hipe/llvm/hipe_llvm.erl
new file mode 100644
index 0000000000..5e33731a2b
--- /dev/null
+++ b/lib/hipe/llvm/hipe_llvm.erl
@@ -0,0 +1,1131 @@
+%% -*- erlang-indent-level: 2 -*-
+
+-module(hipe_llvm).
+
+-export([
+ mk_ret/1,
+ ret_ret_list/1,
+
+ mk_br/1,
+ br_dst/1,
+
+ mk_br_cond/3,
+ mk_br_cond/4,
+ br_cond_cond/1,
+ br_cond_true_label/1,
+ br_cond_false_label/1,
+ br_cond_meta/1,
+
+ mk_indirectbr/3,
+ indirectbr_type/1,
+ indirectbr_address/1,
+ indirectbr_label_list/1,
+
+ mk_switch/4,
+ switch_type/1,
+ switch_value/1,
+ switch_default_label/1,
+ switch_value_label_list/1,
+
+ mk_invoke/9,
+ invoke_dst/1,
+ invoke_cconv/1,
+ invoke_ret_attrs/1,
+ invoke_type/1,
+ invoke_fnptrval/1,
+ invoke_arglist/1,
+ invoke_fn_attrs/1,
+ invoke_to_label/1,
+ invoke_unwind_label/1,
+
+ mk_operation/6,
+ operation_dst/1,
+ operation_op/1,
+ operation_type/1,
+ operation_src1/1,
+ operation_src2/1,
+ operation_options/1,
+
+ mk_extractvalue/5,
+ extractvalue_dst/1,
+ extractvalue_type/1,
+ extractvalue_val/1,
+ extractvalue_idx/1,
+ extractvalue_idxs/1,
+
+ mk_insertvalue/7,
+ insertvalue_dst/1,
+ insertvalue_val_type/1,
+ insertvalue_val/1,
+ insertvalue_elem_type/1,
+ insertvalue_elem/1,
+ insertvalue_idx/1,
+ insertvalue_idxs/1,
+
+ mk_alloca/4,
+ alloca_dst/1,
+ alloca_type/1,
+ alloca_num/1,
+ alloca_align/1,
+
+ mk_load/6,
+ load_dst/1,
+ load_p_type/1,
+ load_pointer/1,
+ load_alignment/1,
+ load_nontemporal/1,
+ load_volatile/1,
+
+ mk_store/7,
+ store_type/1,
+ store_value/1,
+ store_p_type/1,
+ store_pointer/1,
+ store_alignment/1,
+ store_nontemporal/1,
+ store_volatile/1,
+
+ mk_getelementptr/5,
+ getelementptr_dst/1,
+ getelementptr_p_type/1,
+ getelementptr_value/1,
+ getelementptr_typed_idxs/1,
+ getelementptr_inbounds/1,
+
+ mk_conversion/5,
+ conversion_dst/1,
+ conversion_op/1,
+ conversion_src_type/1,
+ conversion_src/1,
+ conversion_dst_type/1,
+
+ mk_sitofp/4,
+ sitofp_dst/1,
+ sitofp_src_type/1,
+ sitofp_src/1,
+ sitofp_dst_type/1,
+
+ mk_ptrtoint/4,
+ ptrtoint_dst/1,
+ ptrtoint_src_type/1,
+ ptrtoint_src/1,
+ ptrtoint_dst_type/1,
+
+ mk_inttoptr/4,
+ inttoptr_dst/1,
+ inttoptr_src_type/1,
+ inttoptr_src/1,
+ inttoptr_dst_type/1,
+
+ mk_icmp/5,
+ icmp_dst/1,
+ icmp_cond/1,
+ icmp_type/1,
+ icmp_src1/1,
+ icmp_src2/1,
+
+ mk_fcmp/5,
+ fcmp_dst/1,
+ fcmp_cond/1,
+ fcmp_type/1,
+ fcmp_src1/1,
+ fcmp_src2/1,
+
+ mk_phi/3,
+ phi_dst/1,
+ phi_type/1,
+ phi_value_label_list/1,
+
+ mk_select/6,
+ select_dst/1,
+ select_cond/1,
+ select_typ1/1,
+ select_val1/1,
+ select_typ2/1,
+ select_val2/1,
+
+ mk_call/8,
+ call_dst/1,
+ call_is_tail/1,
+ call_cconv/1,
+ call_ret_attrs/1,
+ call_type/1,
+ call_fnptrval/1,
+ call_arglist/1,
+ call_fn_attrs/1,
+
+ mk_fun_def/10,
+ fun_def_linkage/1,
+ fun_def_visibility/1,
+ fun_def_cconv/1,
+ fun_def_ret_attrs/1,
+ fun_def_type/1,
+ fun_def_name/1,
+ fun_def_arglist/1,
+ fun_def_fn_attrs/1,
+ fun_def_align/1,
+ fun_def_body/1,
+
+ mk_fun_decl/8,
+ fun_decl_linkage/1,
+ fun_decl_visibility/1,
+ fun_decl_cconv/1,
+ fun_decl_ret_attrs/1,
+ fun_decl_type/1,
+ fun_decl_name/1,
+ fun_decl_arglist/1,
+ fun_decl_align/1,
+
+ mk_landingpad/0,
+
+ mk_comment/1,
+ comment_text/1,
+
+ mk_label/1,
+ label_label/1,
+ is_label/1,
+
+ mk_const_decl/4,
+ const_decl_dst/1,
+ const_decl_decl_type/1,
+ const_decl_type/1,
+ const_decl_value/1,
+
+ mk_asm/1,
+ asm_instruction/1,
+
+ mk_adj_stack/3,
+ adj_stack_offset/1,
+ adj_stack_register/1,
+ adj_stack_type/1,
+
+ mk_branch_meta/3,
+ branch_meta_id/1,
+ branch_meta_true_weight/1,
+ branch_meta_false_weight/1
+ ]).
+
+-export([
+ mk_void/0,
+
+ mk_label_type/0,
+
+ mk_int/1,
+ int_width/1,
+
+ mk_double/0,
+
+ mk_pointer/1,
+ pointer_type/1,
+
+ mk_array/2,
+ array_size/1,
+ array_type/1,
+
+ mk_vector/2,
+ vector_size/1,
+ vector_type/1,
+
+ mk_struct/1,
+ struct_type_list/1,
+
+ mk_fun/2,
+ function_ret_type/1,
+ function_arg_type_list/1
+ ]).
+
+-export([pp_ins_list/2, pp_ins/2]).
+
+
+%%-----------------------------------------------------------------------------
+%% Abstract Data Types for LLVM Assembly.
+%%-----------------------------------------------------------------------------
+
+%% Terminator Instructions
+-record(llvm_ret, {ret_list=[]}).
+-type llvm_ret() :: #llvm_ret{}.
+
+-record(llvm_br, {dst}).
+-type llvm_br() :: #llvm_br{}.
+
+-record(llvm_br_cond, {'cond', true_label, false_label, meta=[]}).
+-type llvm_br_cond() :: #llvm_br_cond{}.
+
+-record(llvm_indirectbr, {type, address, label_list}).
+-type llvm_indirectbr() :: #llvm_indirectbr{}.
+
+-record(llvm_switch, {type, value, default_label, value_label_list=[]}).
+-type llvm_switch() :: #llvm_switch{}.
+
+-record(llvm_invoke, {dst, cconv=[], ret_attrs=[], type, fnptrval, arglist=[],
+ fn_attrs=[], to_label, unwind_label}).
+-type llvm_invoke() :: #llvm_invoke{}.
+
+%% Binary Operations
+-record(llvm_operation, {dst, op, type, src1, src2, options=[]}).
+-type llvm_operation() :: #llvm_operation{}.
+
+%% Aggregate Operations
+-record(llvm_extractvalue, {dst, type, val, idx, idxs=[]}).
+-type llvm_extractvalue() :: #llvm_extractvalue{}.
+
+-record(llvm_insertvalue, {dst, val_type, val, elem_type, elem, idx, idxs=[]}).
+-type llvm_insertvalue() :: #llvm_insertvalue{}.
+
+%% Memory Access and Addressing Operations
+-record(llvm_alloca, {dst, type, num=[], align=[]}).
+-type llvm_alloca() :: #llvm_alloca{}.
+
+-record(llvm_load, {dst, p_type, pointer, alignment=[], nontemporal=[],
+ volatile=false}).
+-type llvm_load() :: #llvm_load{}.
+
+-record(llvm_store, {type, value, p_type, pointer, alignment=[],
+ nontemporal=[], volatile=false}).
+-type llvm_store() :: #llvm_store{}.
+
+-record(llvm_getelementptr, {dst, p_type, value, typed_idxs, inbounds}).
+-type llvm_getelementptr() :: #llvm_getelementptr{}.
+
+%% Conversion Operations
+-record(llvm_conversion, {dst, op, src_type, src, dst_type}).
+-type llvm_conversion() :: #llvm_conversion{}.
+
+-record(llvm_sitofp, {dst, src_type, src, dst_type}).
+-type llvm_sitofp() :: #llvm_sitofp{}.
+
+-record(llvm_ptrtoint, {dst, src_type, src, dst_type}).
+-type llvm_ptrtoint() :: #llvm_ptrtoint{}.
+
+-record(llvm_inttoptr, {dst, src_type, src, dst_type}).
+-type llvm_inttoptr() :: #llvm_inttoptr{}.
+
+%% Other Operations
+-record(llvm_icmp, {dst, 'cond', type, src1, src2}).
+-type llvm_icmp() :: #llvm_icmp{}.
+
+-record(llvm_fcmp, {dst, 'cond', type, src1, src2}).
+-type llvm_fcmp() :: #llvm_fcmp{}.
+
+-record(llvm_phi, {dst, type, value_label_list}).
+-type llvm_phi() :: #llvm_phi{}.
+
+-record(llvm_select, {dst, 'cond', typ1, val1, typ2, val2}).
+-type llvm_select() :: #llvm_select{}.
+
+-record(llvm_call, {dst=[], is_tail = false, cconv = [], ret_attrs = [], type,
+ fnptrval, arglist = [], fn_attrs = []}).
+-type llvm_call() :: #llvm_call{}.
+
+-record(llvm_fun_def, {linkage=[], visibility=[], cconv=[], ret_attrs=[],
+ type, 'name', arglist=[], fn_attrs=[], align=[], body=[]}).
+-type llvm_fun_def() :: #llvm_fun_def{}.
+
+-record(llvm_fun_decl, {linkage=[], visibility=[], cconv=[], ret_attrs=[],
+ type, 'name', arglist=[], align=[]}).
+-type llvm_fun_decl() :: #llvm_fun_decl{}.
+
+-record(llvm_landingpad, {}).
+-type llvm_landingpad() :: #llvm_landingpad{}.
+
+-record(llvm_comment, {text}).
+-type llvm_comment() :: #llvm_comment{}.
+
+-record(llvm_label, {label}).
+-type llvm_label() :: #llvm_label{}.
+
+-record(llvm_const_decl, {dst, decl_type, type, value}).
+-type llvm_const_decl() :: #llvm_const_decl{}.
+
+-record(llvm_asm, {instruction}).
+-type llvm_asm() :: #llvm_asm{}.
+
+-record(llvm_adj_stack, {offset, 'register', type}).
+-type llvm_adj_stack() :: #llvm_adj_stack{}.
+
+-record(llvm_branch_meta, {id, true_weight, false_weight}).
+-type llvm_branch_meta() :: #llvm_branch_meta{}.
+
+%% A type for any LLVM instruction
+-type llvm_instr() :: llvm_ret() | llvm_br() | llvm_br_cond()
+ | llvm_indirectbr() | llvm_switch() | llvm_invoke()
+ | llvm_operation() | llvm_extractvalue()
+ | llvm_insertvalue() | llvm_alloca() | llvm_load()
+ | llvm_store() | llvm_getelementptr() | llvm_conversion()
+ | llvm_sitofp() | llvm_ptrtoint() | llvm_inttoptr()
+ | llvm_icmp() | llvm_fcmp() | llvm_phi() | llvm_select()
+ | llvm_call() | llvm_fun_def() | llvm_fun_decl()
+ | llvm_landingpad() | llvm_comment() | llvm_label()
+ | llvm_const_decl() | llvm_asm() | llvm_adj_stack()
+ | llvm_branch_meta().
+
+%% Types
+-record(llvm_void, {}).
+%-type llvm_void() :: #llvm_void{}.
+
+-record(llvm_label_type, {}).
+%-type llvm_label_type() :: #llvm_label_type{}.
+
+-record(llvm_int, {width}).
+%-type llvm_int() :: #llvm_int{}.
+
+-record(llvm_float, {}).
+%-type llvm_float() :: #llvm_float{}.
+
+-record(llvm_double, {}).
+%-type llvm_double() :: #llvm_double{}.
+
+-record(llvm_fp80, {}).
+%-type llvm_fp80() :: #llvm_fp80{}.
+
+-record(llvm_fp128, {}).
+%-type llvm_fp128() :: #llvm_fp128{}.
+
+-record(llvm_ppc_fp128, {}).
+%-type llvm_ppc_fp128() :: #llvm_ppc_fp128{}.
+
+-record(llvm_pointer, {type}).
+%-type llvm_pointer() :: #llvm_pointer{}.
+
+-record(llvm_vector, {'size', type}).
+%-type llvm_vector() :: #llvm_vector{}.
+
+-record(llvm_struct, {type_list}).
+%-type llvm_struct() :: #llvm_struct{}.
+
+-record(llvm_array, {'size', type}).
+%-type llvm_array() :: #llvm_array{}.
+
+-record(llvm_fun, {ret_type, arg_type_list}).
+%-type llvm_fun() :: #llvm_fun{}.
+
+%%-----------------------------------------------------------------------------
+%% Accessor Functions
+%%-----------------------------------------------------------------------------
+
+%% ret
+mk_ret(Ret_list) -> #llvm_ret{ret_list=Ret_list}.
+ret_ret_list(#llvm_ret{ret_list=Ret_list}) -> Ret_list.
+
+%% br
+mk_br(Dst) -> #llvm_br{dst=Dst}.
+br_dst(#llvm_br{dst=Dst}) -> Dst.
+
+%% br_cond
+mk_br_cond(Cond, True_label, False_label) ->
+ #llvm_br_cond{'cond'=Cond, true_label=True_label, false_label=False_label}.
+mk_br_cond(Cond, True_label, False_label, Metadata) ->
+ #llvm_br_cond{'cond'=Cond, true_label=True_label, false_label=False_label,
+ meta=Metadata}.
+br_cond_cond(#llvm_br_cond{'cond'=Cond}) -> Cond.
+br_cond_true_label(#llvm_br_cond{true_label=True_label}) -> True_label.
+br_cond_false_label(#llvm_br_cond{false_label=False_label}) ->
+ False_label.
+br_cond_meta(#llvm_br_cond{meta=Metadata}) -> Metadata.
+
+%% indirectbr
+mk_indirectbr(Type, Address, Label_list) -> #llvm_indirectbr{type=Type, address=Address, label_list=Label_list}.
+indirectbr_type(#llvm_indirectbr{type=Type}) -> Type.
+indirectbr_address(#llvm_indirectbr{address=Address}) -> Address.
+indirectbr_label_list(#llvm_indirectbr{label_list=Label_list}) -> Label_list.
+
+%% invoke
+mk_invoke(Dst, Cconv, Ret_attrs, Type, Fnptrval, Arglist, Fn_attrs, To_label, Unwind_label) ->
+ #llvm_invoke{dst=Dst, cconv=Cconv, ret_attrs=Ret_attrs, type=Type,
+ fnptrval=Fnptrval, arglist=Arglist, fn_attrs=Fn_attrs, to_label=To_label,
+ unwind_label=Unwind_label}.
+invoke_dst(#llvm_invoke{dst=Dst}) -> Dst.
+invoke_cconv(#llvm_invoke{cconv=Cconv}) -> Cconv.
+invoke_ret_attrs(#llvm_invoke{ret_attrs=Ret_attrs}) -> Ret_attrs.
+invoke_type(#llvm_invoke{type=Type}) -> Type.
+invoke_fnptrval(#llvm_invoke{fnptrval=Fnptrval}) -> Fnptrval.
+invoke_arglist(#llvm_invoke{arglist=Arglist}) -> Arglist.
+invoke_fn_attrs(#llvm_invoke{fn_attrs=Fn_attrs}) -> Fn_attrs.
+invoke_to_label(#llvm_invoke{to_label=To_label}) -> To_label.
+invoke_unwind_label(#llvm_invoke{unwind_label=Unwind_label}) -> Unwind_label.
+
+%% switch
+mk_switch(Type, Value, Default_label, Value_label_list) ->
+ #llvm_switch{type=Type, value=Value, default_label=Default_label,
+ value_label_list=Value_label_list}.
+switch_type(#llvm_switch{type=Type}) -> Type.
+switch_value(#llvm_switch{value=Value}) -> Value.
+switch_default_label(#llvm_switch{default_label=Default_label}) ->
+ Default_label.
+switch_value_label_list(#llvm_switch{value_label_list=Value_label_list}) ->
+ Value_label_list.
+
+%% operation
+mk_operation(Dst, Op, Type, Src1, Src2, Options) ->
+ #llvm_operation{dst=Dst, op=Op, type=Type, src1=Src1, src2=Src2,
+ options=Options}.
+operation_dst(#llvm_operation{dst=Dst}) -> Dst.
+operation_op(#llvm_operation{op=Op}) -> Op.
+operation_type(#llvm_operation{type=Type}) -> Type.
+operation_src1(#llvm_operation{src1=Src1}) -> Src1.
+operation_src2(#llvm_operation{src2=Src2}) -> Src2.
+operation_options(#llvm_operation{options=Options}) -> Options.
+
+%% extractvalue
+mk_extractvalue(Dst, Type, Val, Idx, Idxs) ->
+ #llvm_extractvalue{dst=Dst,type=Type,val=Val,idx=Idx,idxs=Idxs}.
+extractvalue_dst(#llvm_extractvalue{dst=Dst}) -> Dst.
+extractvalue_type(#llvm_extractvalue{type=Type}) -> Type.
+extractvalue_val(#llvm_extractvalue{val=Val}) -> Val.
+extractvalue_idx(#llvm_extractvalue{idx=Idx}) -> Idx.
+extractvalue_idxs(#llvm_extractvalue{idxs=Idxs}) -> Idxs.
+
+%% insertvalue
+mk_insertvalue(Dst, Val_type, Val, Elem_type, Elem, Idx, Idxs) ->
+ #llvm_insertvalue{dst=Dst, val_type=Val_type, val=Val, elem_type=Elem_type,
+ elem=Elem, idx=Idx, idxs=Idxs}.
+insertvalue_dst(#llvm_insertvalue{dst=Dst}) -> Dst.
+insertvalue_val_type(#llvm_insertvalue{val_type=Val_type}) -> Val_type.
+insertvalue_val(#llvm_insertvalue{val=Val}) -> Val.
+insertvalue_elem_type(#llvm_insertvalue{elem_type=Elem_type}) -> Elem_type.
+insertvalue_elem(#llvm_insertvalue{elem=Elem}) -> Elem.
+insertvalue_idx(#llvm_insertvalue{idx=Idx}) -> Idx.
+insertvalue_idxs(#llvm_insertvalue{idxs=Idxs}) -> Idxs.
+
+%% alloca
+mk_alloca(Dst, Type, Num, Align) ->
+ #llvm_alloca{dst=Dst, type=Type, num=Num, align=Align}.
+alloca_dst(#llvm_alloca{dst=Dst}) -> Dst.
+alloca_type(#llvm_alloca{type=Type}) -> Type.
+alloca_num(#llvm_alloca{num=Num}) -> Num.
+alloca_align(#llvm_alloca{align=Align}) -> Align.
+
+%% load
+mk_load(Dst, Type, Pointer, Alignment, Nontemporal, Volatile) ->
+ #llvm_load{dst=Dst, p_type=Type, pointer=Pointer, alignment=Alignment,
+ nontemporal=Nontemporal, volatile=Volatile}.
+load_dst(#llvm_load{dst=Dst}) -> Dst.
+load_p_type(#llvm_load{p_type=Type}) -> Type.
+load_pointer(#llvm_load{pointer=Pointer}) -> Pointer.
+load_alignment(#llvm_load{alignment=Alignment}) -> Alignment.
+load_nontemporal(#llvm_load{nontemporal=Nontemporal}) -> Nontemporal.
+load_volatile(#llvm_load{volatile=Volatile}) -> Volatile.
+
+%% store
+mk_store(Type, Value, P_Type, Pointer, Alignment, Nontemporal, Volatile) ->
+ #llvm_store{type=Type, value=Value, p_type=P_Type, pointer=Pointer, alignment=Alignment,
+ nontemporal=Nontemporal, volatile=Volatile}.
+store_type(#llvm_store{type=Type}) -> Type.
+store_value(#llvm_store{value=Value}) -> Value.
+store_p_type(#llvm_store{p_type=P_Type}) -> P_Type.
+store_pointer(#llvm_store{pointer=Pointer}) -> Pointer.
+store_alignment(#llvm_store{alignment=Alignment}) -> Alignment.
+store_nontemporal(#llvm_store{nontemporal=Nontemporal}) -> Nontemporal.
+store_volatile(#llvm_store{volatile=Volatile}) -> Volatile.
+
+%% getelementptr
+mk_getelementptr(Dst, P_Type, Value, Typed_Idxs, Inbounds) ->
+ #llvm_getelementptr{dst=Dst,p_type=P_Type, value=Value,
+ typed_idxs=Typed_Idxs, inbounds=Inbounds}.
+getelementptr_dst(#llvm_getelementptr{dst=Dst}) -> Dst.
+getelementptr_p_type(#llvm_getelementptr{p_type=P_Type}) -> P_Type.
+getelementptr_value(#llvm_getelementptr{value=Value}) -> Value.
+getelementptr_typed_idxs(#llvm_getelementptr{typed_idxs=Typed_Idxs}) -> Typed_Idxs.
+getelementptr_inbounds(#llvm_getelementptr{inbounds=Inbounds}) -> Inbounds.
+
+%% conversion
+mk_conversion(Dst, Op, Src_type, Src, Dst_type) ->
+ #llvm_conversion{dst=Dst, op=Op, src_type=Src_type, src=Src, dst_type=Dst_type}.
+conversion_dst(#llvm_conversion{dst=Dst}) -> Dst.
+conversion_op(#llvm_conversion{op=Op}) -> Op.
+conversion_src_type(#llvm_conversion{src_type=Src_type}) -> Src_type.
+conversion_src(#llvm_conversion{src=Src}) -> Src.
+conversion_dst_type(#llvm_conversion{dst_type=Dst_type}) -> Dst_type.
+
+%% sitofp
+mk_sitofp(Dst, Src_type, Src, Dst_type) ->
+ #llvm_sitofp{dst=Dst, src_type=Src_type, src=Src, dst_type=Dst_type}.
+sitofp_dst(#llvm_sitofp{dst=Dst}) -> Dst.
+sitofp_src_type(#llvm_sitofp{src_type=Src_type}) -> Src_type.
+sitofp_src(#llvm_sitofp{src=Src}) -> Src.
+sitofp_dst_type(#llvm_sitofp{dst_type=Dst_type}) -> Dst_type.
+
+%% ptrtoint
+mk_ptrtoint(Dst, Src_Type, Src, Dst_Type) ->
+ #llvm_ptrtoint{dst=Dst, src_type=Src_Type, src=Src, dst_type=Dst_Type}.
+ptrtoint_dst(#llvm_ptrtoint{dst=Dst}) -> Dst.
+ptrtoint_src_type(#llvm_ptrtoint{src_type=Src_Type}) -> Src_Type.
+ptrtoint_src(#llvm_ptrtoint{src=Src}) -> Src.
+ptrtoint_dst_type(#llvm_ptrtoint{dst_type=Dst_Type}) -> Dst_Type .
+
+%% inttoptr
+mk_inttoptr(Dst, Src_Type, Src, Dst_Type) ->
+ #llvm_inttoptr{dst=Dst, src_type=Src_Type, src=Src, dst_type=Dst_Type}.
+inttoptr_dst(#llvm_inttoptr{dst=Dst}) -> Dst.
+inttoptr_src_type(#llvm_inttoptr{src_type=Src_Type}) -> Src_Type.
+inttoptr_src(#llvm_inttoptr{src=Src}) -> Src.
+inttoptr_dst_type(#llvm_inttoptr{dst_type=Dst_Type}) -> Dst_Type .
+
+%% icmp
+mk_icmp(Dst, Cond, Type, Src1, Src2) ->
+ #llvm_icmp{dst=Dst,'cond'=Cond,type=Type,src1=Src1,src2=Src2}.
+icmp_dst(#llvm_icmp{dst=Dst}) -> Dst.
+icmp_cond(#llvm_icmp{'cond'=Cond}) -> Cond.
+icmp_type(#llvm_icmp{type=Type}) -> Type.
+icmp_src1(#llvm_icmp{src1=Src1}) -> Src1.
+icmp_src2(#llvm_icmp{src2=Src2}) -> Src2.
+
+%% fcmp
+mk_fcmp(Dst, Cond, Type, Src1, Src2) ->
+ #llvm_fcmp{dst=Dst,'cond'=Cond,type=Type,src1=Src1,src2=Src2}.
+fcmp_dst(#llvm_fcmp{dst=Dst}) -> Dst.
+fcmp_cond(#llvm_fcmp{'cond'=Cond}) -> Cond.
+fcmp_type(#llvm_fcmp{type=Type}) -> Type.
+fcmp_src1(#llvm_fcmp{src1=Src1}) -> Src1.
+fcmp_src2(#llvm_fcmp{src2=Src2}) -> Src2.
+
+%% phi
+mk_phi(Dst, Type, Value_label_list) ->
+ #llvm_phi{dst=Dst, type=Type,value_label_list=Value_label_list}.
+phi_dst(#llvm_phi{dst=Dst}) -> Dst.
+phi_type(#llvm_phi{type=Type}) -> Type.
+phi_value_label_list(#llvm_phi{value_label_list=Value_label_list}) ->
+ Value_label_list.
+
+%% select
+mk_select(Dst, Cond, Typ1, Val1, Typ2, Val2) ->
+ #llvm_select{dst=Dst, 'cond'=Cond, typ1=Typ1, val1=Val1, typ2=Typ2, val2=Val2}.
+select_dst(#llvm_select{dst=Dst}) -> Dst.
+select_cond(#llvm_select{'cond'=Cond}) -> Cond.
+select_typ1(#llvm_select{typ1=Typ1}) -> Typ1.
+select_val1(#llvm_select{val1=Val1}) -> Val1.
+select_typ2(#llvm_select{typ2=Typ2}) -> Typ2.
+select_val2(#llvm_select{val2=Val2}) -> Val2.
+
+%% call
+mk_call(Dst, Is_tail, Cconv, Ret_attrs, Type, Fnptrval, Arglist, Fn_attrs) ->
+ #llvm_call{dst=Dst, is_tail=Is_tail, cconv=Cconv, ret_attrs=Ret_attrs,
+ type=Type, fnptrval=Fnptrval, arglist=Arglist, fn_attrs=Fn_attrs}.
+call_dst(#llvm_call{dst=Dst}) -> Dst.
+call_is_tail(#llvm_call{is_tail=Is_tail}) -> Is_tail.
+call_cconv(#llvm_call{cconv=Cconv}) -> Cconv.
+call_ret_attrs(#llvm_call{ret_attrs=Ret_attrs}) -> Ret_attrs.
+call_type(#llvm_call{type=Type}) -> Type.
+call_fnptrval(#llvm_call{fnptrval=Fnptrval}) -> Fnptrval.
+call_arglist(#llvm_call{arglist=Arglist}) -> Arglist.
+call_fn_attrs(#llvm_call{fn_attrs=Fn_attrs}) -> Fn_attrs.
+
+%% fun_def
+mk_fun_def(Linkage, Visibility, Cconv, Ret_attrs, Type, Name, Arglist,
+ Fn_attrs, Align, Body) ->
+ #llvm_fun_def{
+ linkage=Linkage,
+ visibility=Visibility,
+ cconv=Cconv,
+ ret_attrs=Ret_attrs,
+ type=Type,
+ 'name'=Name,
+ arglist=Arglist,
+ fn_attrs=Fn_attrs,
+ align=Align,
+ body=Body
+ }.
+
+fun_def_linkage(#llvm_fun_def{linkage=Linkage}) -> Linkage.
+fun_def_visibility(#llvm_fun_def{visibility=Visibility}) -> Visibility.
+fun_def_cconv(#llvm_fun_def{cconv=Cconv}) -> Cconv .
+fun_def_ret_attrs(#llvm_fun_def{ret_attrs=Ret_attrs}) -> Ret_attrs.
+fun_def_type(#llvm_fun_def{type=Type}) -> Type.
+fun_def_name(#llvm_fun_def{'name'=Name}) -> Name.
+fun_def_arglist(#llvm_fun_def{arglist=Arglist}) -> Arglist.
+fun_def_fn_attrs(#llvm_fun_def{fn_attrs=Fn_attrs}) -> Fn_attrs.
+fun_def_align(#llvm_fun_def{align=Align}) -> Align.
+fun_def_body(#llvm_fun_def{body=Body}) -> Body.
+
+%% fun_decl
+mk_fun_decl(Linkage, Visibility, Cconv, Ret_attrs, Type, Name, Arglist, Align)->
+ #llvm_fun_decl{
+ linkage=Linkage,
+ visibility=Visibility,
+ cconv=Cconv,
+ ret_attrs=Ret_attrs,
+ type=Type,
+ 'name'=Name,
+ arglist=Arglist,
+ align=Align
+ }.
+
+fun_decl_linkage(#llvm_fun_decl{linkage=Linkage}) -> Linkage.
+fun_decl_visibility(#llvm_fun_decl{visibility=Visibility}) -> Visibility.
+fun_decl_cconv(#llvm_fun_decl{cconv=Cconv}) -> Cconv .
+fun_decl_ret_attrs(#llvm_fun_decl{ret_attrs=Ret_attrs}) -> Ret_attrs.
+fun_decl_type(#llvm_fun_decl{type=Type}) -> Type.
+fun_decl_name(#llvm_fun_decl{'name'=Name}) -> Name.
+fun_decl_arglist(#llvm_fun_decl{arglist=Arglist}) -> Arglist.
+fun_decl_align(#llvm_fun_decl{align=Align}) -> Align.
+
+%% landingpad
+mk_landingpad() -> #llvm_landingpad{}.
+
+%% comment
+mk_comment(Text) -> #llvm_comment{text=Text}.
+comment_text(#llvm_comment{text=Text}) -> Text.
+
+%% label
+mk_label(Label) -> #llvm_label{label=Label}.
+label_label(#llvm_label{label=Label}) -> Label.
+
+-spec is_label(llvm_instr()) -> boolean().
+is_label(#llvm_label{}) -> true;
+is_label(#llvm_ret{}) -> false;
+is_label(#llvm_br{}) -> false;
+is_label(#llvm_br_cond{}) -> false;
+is_label(#llvm_indirectbr{}) -> false;
+is_label(#llvm_switch{}) -> false;
+is_label(#llvm_invoke{}) -> false;
+is_label(#llvm_operation{}) -> false;
+is_label(#llvm_extractvalue{}) -> false;
+is_label(#llvm_insertvalue{}) -> false;
+is_label(#llvm_alloca{}) -> false;
+is_label(#llvm_load{}) -> false;
+is_label(#llvm_store{}) -> false;
+is_label(#llvm_getelementptr{}) -> false;
+is_label(#llvm_conversion{}) -> false;
+is_label(#llvm_sitofp{}) -> false;
+is_label(#llvm_ptrtoint{}) -> false;
+is_label(#llvm_inttoptr{}) -> false;
+is_label(#llvm_icmp{}) -> false;
+is_label(#llvm_fcmp{}) -> false;
+is_label(#llvm_phi{}) -> false;
+is_label(#llvm_select{}) -> false;
+is_label(#llvm_call{}) -> false;
+is_label(#llvm_fun_def{}) -> false;
+is_label(#llvm_fun_decl{}) -> false;
+is_label(#llvm_landingpad{}) -> false;
+is_label(#llvm_comment{}) -> false;
+is_label(#llvm_const_decl{}) -> false;
+is_label(#llvm_asm{}) -> false;
+is_label(#llvm_adj_stack{}) -> false;
+is_label(#llvm_branch_meta{}) -> false.
+
+%% const_decl
+mk_const_decl(Dst, Decl_type, Type, Value) ->
+ #llvm_const_decl{dst=Dst, decl_type=Decl_type, type=Type, value=Value}.
+const_decl_dst(#llvm_const_decl{dst=Dst}) -> Dst.
+const_decl_decl_type(#llvm_const_decl{decl_type=Decl_type}) -> Decl_type.
+const_decl_type(#llvm_const_decl{type=Type}) -> Type.
+const_decl_value(#llvm_const_decl{value=Value}) -> Value.
+
+%% asm
+mk_asm(Instruction) -> #llvm_asm{instruction=Instruction}.
+asm_instruction(#llvm_asm{instruction=Instruction}) -> Instruction.
+
+%% adj_stack
+mk_adj_stack(Offset, Register, Type) ->
+ #llvm_adj_stack{offset=Offset, 'register'=Register, type=Type}.
+adj_stack_offset(#llvm_adj_stack{offset=Offset}) -> Offset.
+adj_stack_register(#llvm_adj_stack{'register'=Register}) -> Register.
+adj_stack_type(#llvm_adj_stack{type=Type}) -> Type.
+
+%% branch meta-data
+mk_branch_meta(Id, True_weight, False_weight) ->
+ #llvm_branch_meta{id=Id, true_weight=True_weight, false_weight=False_weight}.
+branch_meta_id(#llvm_branch_meta{id=Id}) -> Id.
+branch_meta_true_weight(#llvm_branch_meta{true_weight=True_weight}) ->
+ True_weight.
+branch_meta_false_weight(#llvm_branch_meta{false_weight=False_weight}) ->
+ False_weight.
+
+%% types
+mk_void() -> #llvm_void{}.
+
+mk_label_type() -> #llvm_label_type{}.
+
+mk_int(Width) -> #llvm_int{width=Width}.
+int_width(#llvm_int{width=Width}) -> Width.
+
+mk_double() -> #llvm_double{}.
+
+mk_pointer(Type) -> #llvm_pointer{type=Type}.
+pointer_type(#llvm_pointer{type=Type}) -> Type.
+
+mk_array(Size, Type) -> #llvm_array{'size'=Size, type=Type}.
+array_size(#llvm_array{'size'=Size}) -> Size.
+array_type(#llvm_array{type=Type}) -> Type.
+
+mk_vector(Size, Type) -> #llvm_vector{'size'=Size, type=Type}.
+vector_size(#llvm_vector{'size'=Size}) -> Size.
+vector_type(#llvm_vector{type=Type}) -> Type.
+
+mk_struct(Type_list) -> #llvm_struct{type_list=Type_list}.
+struct_type_list(#llvm_struct{type_list=Type_list}) -> Type_list.
+
+mk_fun(Ret_type, Arg_type_list) ->
+ #llvm_fun{ret_type=Ret_type, arg_type_list=Arg_type_list}.
+function_ret_type(#llvm_fun{ret_type=Ret_type}) -> Ret_type.
+function_arg_type_list(#llvm_fun{arg_type_list=Arg_type_list}) ->
+ Arg_type_list.
+
+%%----------------------------------------------------------------------------
+%% Pretty-printer Functions
+%%----------------------------------------------------------------------------
+
+%% @doc Pretty-print a list of LLVM instructions to a Device.
+pp_ins_list(_Dev, []) -> ok;
+pp_ins_list(Dev, [I|Is]) ->
+ pp_ins(Dev, I),
+ pp_ins_list(Dev, Is).
+
+pp_ins(Dev, I) ->
+ case indent(I) of
+ true -> write(Dev, " ");
+ false -> ok
+ end,
+ case I of
+ #llvm_ret{} ->
+ write(Dev, "ret "),
+ case ret_ret_list(I) of
+ [] -> write(Dev, "void");
+ List -> pp_args(Dev, List)
+ end,
+ write(Dev, "\n");
+ #llvm_br{} ->
+ write(Dev, ["br label ", br_dst(I), "\n"]);
+ #llvm_switch{} ->
+ write(Dev, "switch "),
+ pp_type(Dev, switch_type(I)),
+ write(Dev, [" ", switch_value(I), ", label ", switch_default_label(I),
+ " \n [\n"]),
+ pp_switch_value_label_list(Dev, switch_type(I),
+ switch_value_label_list(I)),
+ write(Dev, " ]\n");
+ #llvm_invoke{} ->
+ write(Dev, [invoke_dst(I), " = invoke ", invoke_cconv(I), " "]),
+ pp_options(Dev, invoke_ret_attrs(I)),
+ pp_type(Dev, invoke_type(I)),
+ write(Dev, [" ", invoke_fnptrval(I), "("]),
+ pp_args(Dev, invoke_arglist(I)),
+ write(Dev, ") "),
+ pp_options(Dev, invoke_fn_attrs(I)),
+ write(Dev, [" to label ", invoke_to_label(I)," unwind label ",
+ invoke_unwind_label(I), " \n"]);
+ #llvm_br_cond{} ->
+ write(Dev, ["br i1 ", br_cond_cond(I), ", label ", br_cond_true_label(I),
+ ", label ", br_cond_false_label(I)]),
+ case br_cond_meta(I) of
+ [] -> ok;
+ Metadata ->
+ write(Dev, [", !prof !", Metadata])
+ end,
+ write(Dev, "\n");
+ #llvm_indirectbr{} ->
+ write(Dev, "indirectbr "),
+ pp_type(Dev, indirectbr_type(I)),
+ write(Dev, [" ", indirectbr_address(I), ", [ "]),
+ pp_args(Dev, indirectbr_label_list(I)),
+ write(Dev, " ]\n");
+ #llvm_operation{} ->
+ write(Dev, [operation_dst(I), " = ", atom_to_list(operation_op(I)), " "]),
+ case op_has_options(operation_op(I)) of
+ true -> pp_options(Dev, operation_options(I));
+ false -> ok
+ end,
+ pp_type(Dev, operation_type(I)),
+ write(Dev, [" ", operation_src1(I), ", ", operation_src2(I), "\n"]);
+ #llvm_extractvalue{} ->
+ write(Dev, [extractvalue_dst(I), " = extractvalue "]),
+ pp_type(Dev, extractvalue_type(I)),
+ %% TODO Print idxs
+ write(Dev, [" ", extractvalue_val(I), ", ", extractvalue_idx(I), "\n"]);
+ #llvm_insertvalue{} ->
+ write(Dev, [insertvalue_dst(I), " = insertvalue "]),
+ pp_type(Dev, insertvalue_val_type(I)),
+ write(Dev, [" ", insertvalue_val(I), ", "]),
+ pp_type(Dev, insertvalue_elem_type(I)),
+ %%TODO Print idxs
+ write(Dev, [" ", insertvalue_elem(I), ", ", insertvalue_idx(I), "\n"]);
+ #llvm_alloca{} ->
+ write(Dev, [alloca_dst(I), " = alloca "]),
+ pp_type(Dev, alloca_type(I)),
+ case alloca_num(I) of
+ [] -> ok;
+ Num ->
+ write(Dev, ", "),
+ pp_type(Dev, alloca_type(I)),
+ write(Dev, [" ", Num, " "])
+ end,
+ case alloca_align(I) of
+ [] -> ok;
+ Align -> write(Dev, [",align ", Align])
+ end,
+ write(Dev, "\n");
+ #llvm_load{} ->
+ write(Dev, [load_dst(I), " = "]),
+ write(Dev, "load "),
+ case load_volatile(I) of
+ true -> write(Dev, "volatile ");
+ false -> ok
+ end,
+ pp_type(Dev, load_p_type(I)),
+ write(Dev, [" ", load_pointer(I), " "]),
+ case load_alignment(I) of
+ [] -> ok;
+ Al -> write(Dev, [", align ", Al, " "])
+ end,
+ case load_nontemporal(I) of
+ [] -> ok;
+ In -> write(Dev, [", !nontemporal !", In])
+ end,
+ write(Dev, "\n");
+ #llvm_store{} ->
+ write(Dev, "store "),
+ case store_volatile(I) of
+ true -> write(Dev, "volatile ");
+ false -> ok
+ end,
+ pp_type(Dev, store_type(I)),
+ write(Dev, [" ", store_value(I), ", "]),
+ pp_type(Dev, store_p_type(I)),
+ write(Dev, [" ", store_pointer(I), " "]),
+ case store_alignment(I) of
+ [] -> ok;
+ Al -> write(Dev, [", align ", Al, " "])
+ end,
+ case store_nontemporal(I) of
+ [] -> ok;
+ In -> write(Dev, [", !nontemporal !", In])
+ end,
+ write(Dev, "\n");
+ #llvm_getelementptr{} ->
+ write(Dev, [getelementptr_dst(I), " = getelementptr "]),
+ case getelementptr_inbounds(I) of
+ true -> write(Dev, "inbounds ");
+ false -> ok
+ end,
+ pp_type(Dev, getelementptr_p_type(I)),
+ write(Dev, [" ", getelementptr_value(I)]),
+ pp_typed_idxs(Dev, getelementptr_typed_idxs(I)),
+ write(Dev, "\n");
+ #llvm_conversion{} ->
+ write(Dev, [conversion_dst(I), " = ", atom_to_list(conversion_op(I)), " "]),
+ pp_type(Dev, conversion_src_type(I)),
+ write(Dev, [" ", conversion_src(I), " to "]),
+ pp_type(Dev, conversion_dst_type(I)),
+ write(Dev, "\n");
+ #llvm_icmp{} ->
+ write(Dev, [icmp_dst(I), " = icmp ", atom_to_list(icmp_cond(I)), " "]),
+ pp_type(Dev, icmp_type(I)),
+ write(Dev, [" ", icmp_src1(I), ", ", icmp_src2(I), "\n"]);
+ #llvm_fcmp{} ->
+ write(Dev, [fcmp_dst(I), " = fcmp ", atom_to_list(fcmp_cond(I)), " "]),
+ pp_type(Dev, fcmp_type(I)),
+ write(Dev, [" ", fcmp_src1(I), ", ", fcmp_src2(I), "\n"]);
+ #llvm_phi{} ->
+ write(Dev, [phi_dst(I), " = phi "]),
+ pp_type(Dev, phi_type(I)),
+ pp_phi_value_labels(Dev, phi_value_label_list(I)),
+ write(Dev, "\n");
+ #llvm_select{} ->
+ write(Dev, [select_dst(I), " = select i1 ", select_cond(I), ", "]),
+ pp_type(Dev, select_typ1(I)),
+ write(Dev, [" ", select_val1(I), ", "]),
+ pp_type(Dev, select_typ2(I)),
+ write(Dev, [" ", select_val2(I), "\n"]);
+ #llvm_call{} ->
+ case call_dst(I) of
+ [] -> ok;
+ Dst -> write(Dev, [Dst, " = "])
+ end,
+ case call_is_tail(I) of
+ true -> write(Dev, "tail ");
+ false -> ok
+ end,
+ write(Dev, ["call ", call_cconv(I), " "]),
+ pp_options(Dev, call_ret_attrs(I)),
+ pp_type(Dev, call_type(I)),
+ write(Dev, [" ", call_fnptrval(I), "("]),
+ pp_args(Dev, call_arglist(I)),
+ write(Dev, ") "),
+ pp_options(Dev, call_fn_attrs(I)),
+ write(Dev, "\n");
+ #llvm_fun_def{} ->
+ write(Dev, "define "),
+ pp_options(Dev, fun_def_linkage(I)),
+ pp_options(Dev, fun_def_visibility(I)),
+ case fun_def_cconv(I) of
+ [] -> ok;
+ Cc -> write(Dev, [Cc, " "])
+ end,
+ pp_options(Dev, fun_def_ret_attrs(I)),
+ write(Dev, " "),
+ pp_type(Dev, fun_def_type(I)),
+ write(Dev, [" @", fun_def_name(I), "("]),
+ pp_args(Dev, fun_def_arglist(I)),
+ write(Dev, ") "),
+ pp_options(Dev, fun_def_fn_attrs(I)),
+ case fun_def_align(I) of
+ [] -> ok;
+ N -> write(Dev, ["align ", N])
+ end,
+ write(Dev, "{\n"),
+ pp_ins_list(Dev, fun_def_body(I)),
+ write(Dev, "}\n");
+ #llvm_fun_decl{} ->
+ write(Dev, "declare "),
+ pp_options(Dev, fun_decl_linkage(I)),
+ pp_options(Dev, fun_decl_visibility(I)),
+ case fun_decl_cconv(I) of
+ [] -> ok;
+ Cc -> write(Dev, [Cc, " "])
+ end,
+ pp_options(Dev, fun_decl_ret_attrs(I)),
+ pp_type(Dev, fun_decl_type(I)),
+ write(Dev, [" ", fun_decl_name(I), "("]),
+ pp_type_list(Dev, fun_decl_arglist(I)),
+ write(Dev, ") "),
+ case fun_decl_align(I) of
+ [] -> ok;
+ N -> write(Dev, ["align ", N])
+ end,
+ write(Dev, "\n");
+ #llvm_comment{} ->
+ write(Dev, ["; ", atom_to_list(comment_text(I)), "\n"]);
+ #llvm_label{} ->
+ write(Dev, [label_label(I), ":\n"]);
+ #llvm_const_decl{} ->
+ write(Dev, [const_decl_dst(I), " = ", const_decl_decl_type(I), " "]),
+ pp_type(Dev, const_decl_type(I)),
+ write(Dev, [" ", const_decl_value(I), "\n"]);
+ #llvm_landingpad{} ->
+ write(Dev, "landingpad { i8*, i32 } personality i32 (i32, i64, i8*,i8*)*
+ @__gcc_personality_v0 cleanup\n");
+ #llvm_asm{} ->
+ write(Dev, [asm_instruction(I), "\n"]);
+ #llvm_adj_stack{} ->
+ write(Dev, ["call void asm sideeffect \"sub $0, ",
+ adj_stack_register(I), "\", \"r\"("]),
+ pp_type(Dev, adj_stack_type(I)),
+ write(Dev, [" ", adj_stack_offset(I),")\n"]);
+ #llvm_branch_meta{} ->
+ write(Dev, ["!", branch_meta_id(I), " = metadata !{metadata !\"branch_weights\",
+ i32 ", branch_meta_true_weight(I), ", i32 ",
+ branch_meta_false_weight(I), "}\n"]);
+ Other ->
+ exit({?MODULE, pp_ins, {"Unknown LLVM instruction", Other}})
+ end.
+
+%% @doc Pretty-print a list of types
+pp_type_list(_Dev, []) -> ok;
+pp_type_list(Dev, [T]) ->
+ pp_type(Dev, T);
+pp_type_list(Dev, [T|Ts]) ->
+ pp_type(Dev, T),
+ write(Dev, ", "),
+ pp_type_list(Dev, Ts).
+
+pp_type(Dev, Type) ->
+ case Type of
+ #llvm_void{} ->
+ write(Dev, "void");
+ #llvm_label_type{} ->
+ write(Dev, "label");
+ %% Integer
+ #llvm_int{} ->
+ write(Dev, ["i", integer_to_list(int_width(Type))]);
+ %% Float
+ #llvm_float{} ->
+ write(Dev, "float");
+ #llvm_double{} ->
+ write(Dev, "double");
+ #llvm_fp80{} ->
+ write(Dev, "x86_fp80");
+ #llvm_fp128{} ->
+ write(Dev, "fp128");
+ #llvm_ppc_fp128{} ->
+ write(Dev, "ppc_fp128");
+ %% Pointer
+ #llvm_pointer{} ->
+ pp_type(Dev, pointer_type(Type)),
+ write(Dev, "*");
+ %% Function
+ #llvm_fun{} ->
+ pp_type(Dev, function_ret_type(Type)),
+ write(Dev, " ("),
+ pp_type_list(Dev, function_arg_type_list(Type)),
+ write(Dev, ")");
+ %% Aggregate
+ #llvm_array{} ->
+ write(Dev, ["[", integer_to_list(array_size(Type)), " x "]),
+ pp_type(Dev, array_type(Type)),
+ write(Dev, "]");
+ #llvm_struct{} ->
+ write(Dev, "{"),
+ pp_type_list(Dev, struct_type_list(Type)),
+ write(Dev, "}");
+ #llvm_vector{} ->
+ write(Dev, ["{", integer_to_list(vector_size(Type)), " x "]),
+ pp_type(Dev, vector_type(Type)),
+ write(Dev, "}")
+ end.
+
+%% @doc Pretty-print a list of typed arguments
+pp_args(_Dev, []) -> ok;
+pp_args(Dev, [{Type, Arg} | []]) ->
+ pp_type(Dev, Type),
+ write(Dev, [" ", Arg]);
+pp_args(Dev, [{Type, Arg} | Args]) ->
+ pp_type(Dev, Type),
+ write(Dev, [" ", Arg, ", "]),
+ pp_args(Dev, Args).
+
+%% @doc Pretty-print a list of options
+pp_options(_Dev, []) -> ok;
+pp_options(Dev, [O|Os]) ->
+ write(Dev, [atom_to_list(O), " "]),
+ pp_options(Dev, Os).
+
+%% @doc Pretty-print a list of phi value-labels
+pp_phi_value_labels(_Dev, []) -> ok;
+pp_phi_value_labels(Dev, [{Value, Label}|[]]) ->
+ write(Dev, ["[ ", Value, ", ", Label, " ]"]);
+pp_phi_value_labels(Dev,[{Value, Label}|VL]) ->
+ write(Dev, ["[ ", Value, ", ", Label, " ], "]),
+ pp_phi_value_labels(Dev, VL).
+
+%% @doc Pretty-print a list of typed indexes
+pp_typed_idxs(_Dev, []) -> ok;
+pp_typed_idxs(Dev, [{Type, Id} | Tids]) ->
+ write(Dev, ", "),
+ pp_type(Dev, Type),
+ write(Dev, [" ", Id]),
+ pp_typed_idxs(Dev, Tids).
+
+%% @doc Pretty-print a switch label list
+pp_switch_value_label_list(_Dev, _Type, []) -> ok;
+pp_switch_value_label_list(Dev, Type, [{Value, Label} | VLs]) ->
+ write(Dev, " "),
+ pp_type(Dev, Type),
+ write(Dev, [" ", Value, ", label ", Label, "\n"]),
+ pp_switch_value_label_list(Dev, Type, VLs).
+
+%%----------------------------------------------------------------------------
+%% Auxiliary Functions
+%%----------------------------------------------------------------------------
+
+%% @doc Returns if an instruction needs to be intended
+indent(I) ->
+ case I of
+ #llvm_label{} -> false;
+ #llvm_fun_def{} -> false;
+ #llvm_fun_decl{} -> false;
+ #llvm_const_decl{} -> false;
+ #llvm_branch_meta{} -> false;
+ _ -> true
+ end.
+
+op_has_options(Op) ->
+ case Op of
+ 'and' -> false;
+ 'or' -> false;
+ 'xor' -> false;
+ _ -> true
+ end.
+
+%% @doc Abstracts actual writing to file operations
+write(Dev, Msg) ->
+ ok = file:write(Dev, Msg).
diff --git a/lib/hipe/llvm/hipe_llvm_arch.hrl b/lib/hipe/llvm/hipe_llvm_arch.hrl
new file mode 100644
index 0000000000..689a5a52ea
--- /dev/null
+++ b/lib/hipe/llvm/hipe_llvm_arch.hrl
@@ -0,0 +1,11 @@
+-ifdef(BIT32).
+-define(NR_PINNED_REGS, 2).
+-define(NR_ARG_REGS, 3).
+-define(ARCH_REGISTERS, hipe_x86_registers).
+-define(FLOAT_OFFSET, 2).
+-else.
+-define(NR_PINNED_REGS, 2).
+-define(NR_ARG_REGS, 4).
+-define(ARCH_REGISTERS, hipe_amd64_registers).
+-define(FLOAT_OFFSET, 6).
+-endif.
diff --git a/lib/hipe/llvm/hipe_llvm_liveness.erl b/lib/hipe/llvm/hipe_llvm_liveness.erl
new file mode 100644
index 0000000000..d1c90ed4c9
--- /dev/null
+++ b/lib/hipe/llvm/hipe_llvm_liveness.erl
@@ -0,0 +1,112 @@
+-module(hipe_llvm_liveness).
+
+-export([analyze/1]).
+
+%% @doc Find gc roots and explicitly mark when they go out of scope, based
+%% on the liveness analyzis performed by the hipe_rtl_liveness:analyze/1.
+analyze(RtlCfg) ->
+ Liveness = hipe_rtl_liveness:analyze(RtlCfg),
+ Roots = find_roots(RtlCfg, Liveness),
+ %% erlang:display(Roots),
+ NewRtlCfg = mark_dead_roots(RtlCfg, Liveness, Roots),
+ {NewRtlCfg, Roots}.
+
+%% @doc Determine which are the GC Roots.Possible roots are all
+%% RTL variables (rtl_var). However, since safe points are function calls, we
+%% consider as possible GC roots only RTL variables that are live around
+%% function calls.
+find_roots(Cfg, Liveness) ->
+ Labels = hipe_rtl_cfg:postorder(Cfg),
+ Roots = find_roots_bb(Labels, Cfg, Liveness, []),
+ lists:usort(lists:flatten(Roots)).
+
+find_roots_bb([], _Cfg, _Liveness, RootAcc) ->
+ RootAcc;
+find_roots_bb([L|Ls], Cfg, Liveness, RootAcc) ->
+ Block = hipe_rtl_cfg:bb(Cfg, L),
+ BlockCode = hipe_bb:code(Block),
+ LiveIn = ordsets:from_list(strip(hipe_rtl_liveness:livein(Liveness, L))),
+ LiveOut = ordsets:from_list(strip(hipe_rtl_liveness:liveout(Liveness, L))),
+ Roots = do_find_roots_bb(BlockCode, L, LiveOut, LiveIn, []),
+ find_roots_bb(Ls, Cfg, Liveness, Roots++RootAcc).
+
+%% For each call inside a BB the GC roots are those RTL variables that
+%% are live before and after the call.
+%% --> Live Before Call: These are the RTL variables that belong to the
+%% LiveIn list or are initialized inside the BB before the call
+%% --> Live After Call: These are the RTL variables that belong to the
+%% LiveOut list or are used after the call inside the BB (they die
+%% inside the BB and so do not belong to the LiveOut list)
+do_find_roots_bb([], _Label, _LiveOut, _LiveBefore, RootAcc) ->
+ RootAcc;
+do_find_roots_bb([I|Is], L, LiveOut, LiveBefore, RootAcc) ->
+ case hipe_rtl:is_call(I) of
+ true ->
+ %% Used inside the BB after the call
+ UsedAfterCall_ = strip(lists:flatten([hipe_rtl:uses(V) || V <- Is])),
+ UsedAfterCall = ordsets:from_list(UsedAfterCall_),
+ LiveAfter = ordsets:union(UsedAfterCall, LiveOut),
+ %% The Actual Roots
+ Roots = ordsets:intersection(LiveBefore, LiveAfter),
+ %% The result of the instruction
+ Defines = ordsets:from_list(strip(hipe_rtl:defines(I))),
+ LiveBefore1 = ordsets:union(LiveBefore, Defines),
+ do_find_roots_bb(Is, L, LiveOut, LiveBefore1, [Roots|RootAcc]);
+ false ->
+ %% The result of the instruction
+ Defines = ordsets:from_list(strip(hipe_rtl:defines(I))),
+ LiveBefore1 = ordsets:union(LiveBefore, Defines),
+ do_find_roots_bb(Is, L, LiveOut, LiveBefore1, RootAcc)
+ end.
+
+%% @doc This function is responsible for marking when GC Roots, which can be
+%% only RTL variables go out of scope (dead). This pass is needed for the LLVM
+%% back end because the LLVM framework forces us to explicit mark when gc roots
+%% are no longer live.
+mark_dead_roots(CFG, Liveness, Roots) ->
+ Labels = hipe_rtl_cfg:postorder(CFG),
+ mark_dead_bb(Labels, CFG, Liveness, Roots).
+
+mark_dead_bb([], Cfg, _Liveness, _Roots) ->
+ Cfg;
+mark_dead_bb([L|Ls], Cfg, Liveness, Roots) ->
+ Block = hipe_rtl_cfg:bb(Cfg, L),
+ BlockCode = hipe_bb:code(Block),
+ LiveOut = ordsets:from_list(strip(hipe_rtl_liveness:liveout(Liveness, L))),
+ NewBlockCode = do_mark_dead_bb(BlockCode, LiveOut, Roots, []),
+ %% Update the CFG
+ NewBB = hipe_bb:code_update(Block, NewBlockCode),
+ NewCFG = hipe_rtl_cfg:bb_add(Cfg, L, NewBB),
+ mark_dead_bb(Ls, NewCFG, Liveness, Roots).
+
+do_mark_dead_bb([], _LiveOut, _Roots, NewBlockCode) ->
+ lists:reverse(NewBlockCode);
+do_mark_dead_bb([I|Is], LiveOut ,Roots, NewBlockCode) ->
+ Uses = ordsets:from_list(strip(hipe_rtl:uses(I))),
+ %% GC roots that are used in this instruction
+ RootsUsed = ordsets:intersection(Roots, Uses),
+ UsedAfter_ = strip(lists:flatten([hipe_rtl:uses(V) || V <- Is])),
+ UsedAfter = ordsets:from_list(UsedAfter_),
+ %% GC roots that are live after this instruction
+ LiveAfter = ordsets:union(LiveOut, UsedAfter),
+ %% GC roots that their last use is in this instruction
+ DeadRoots = ordsets:subtract(RootsUsed, LiveAfter),
+ %% Recreate the RTL variable from the corresponding Index
+ OldVars = [hipe_rtl:mk_var(V1) || V1 <- DeadRoots],
+ %% Mark the RTL variable as DEAD (last use)
+ NewVars = [kill_var(V2) || V2 <- OldVars],
+ %% Create a list with the substitution of the old vars with the new
+ %% ones which are marked with the dead keyword
+ Subtitution = lists:zip(OldVars, NewVars),
+ NewI = case Subtitution of
+ [] -> I;
+ _ -> hipe_rtl:subst_uses_llvm(Subtitution, I)
+ end,
+ do_mark_dead_bb(Is, LiveOut, Roots, [NewI|NewBlockCode]).
+
+%% Update the liveness of a var,in order to mark that this is the last use.
+kill_var(Var) -> hipe_rtl:var_liveness_update(Var, dead).
+
+%% We are only interested for rtl_vars, since only rtl_vars are possible gc
+%% roots.
+strip(L) -> [Y || {rtl_var, Y, _} <- L].
diff --git a/lib/hipe/llvm/hipe_llvm_main.erl b/lib/hipe/llvm/hipe_llvm_main.erl
new file mode 100644
index 0000000000..0e50c9539b
--- /dev/null
+++ b/lib/hipe/llvm/hipe_llvm_main.erl
@@ -0,0 +1,506 @@
+%% -*- erlang-indent-level: 2 -*-
+-module(hipe_llvm_main).
+
+-export([rtl_to_native/4]).
+
+-include("../../kernel/src/hipe_ext_format.hrl").
+-include("hipe_llvm_arch.hrl").
+-include("elf_format.hrl").
+
+%% @doc Translation of RTL to a loadable object. This function takes the RTL
+%% code and calls hipe_rtl_to_llvm:translate/2 to translate the RTL code to
+%% LLVM code. After this, LLVM asm is printed to a file and the LLVM tool
+%% chain is invoked in order to produce an object file.
+rtl_to_native(MFA, RTL, Roots, Options) ->
+ %% Compile to LLVM and get Instruction List (along with infos)
+ {LLVMCode, RelocsDict, ConstTab} =
+ hipe_rtl_to_llvm:translate(RTL, Roots),
+ %% Fix function name to an acceptable LLVM identifier (needed for closures)
+ {_Module, Fun, Arity} = hipe_rtl_to_llvm:fix_mfa_name(MFA),
+ %% Write LLVM Assembly to intermediate file (on disk)
+ {ok, Dir, ObjectFile} =
+ compile_with_llvm(Fun, Arity, LLVMCode, Options, false),
+ %%
+ %% Extract information from object file
+ %%
+ ObjBin = open_object_file(ObjectFile),
+ %% Read and set the ELF class
+ elf_format:set_architecture_flag(ObjBin),
+ %% Get labels info (for switches and jump tables)
+ Labels = elf_format:get_rodata_relocs(ObjBin),
+ {Switches, Closures} = get_tables(ObjBin),
+ %% Associate Labels with Switches and Closures with stack args
+ {SwitchInfos, ExposedClosures} =
+ correlate_labels(Switches ++ Closures, Labels),
+ %% SwitchInfos: [{"table_50", [Labels]}]
+ %% ExposedClosures: [{"table_closures", [Labels]}]
+
+ %% Labelmap contains the offsets of the labels in the code that are
+ %% used for switch's jump tables
+ LabelMap = create_labelmap(MFA, SwitchInfos, RelocsDict),
+ %% Get relocation info
+ TextRelocs = elf_format:get_text_relocs(ObjBin),
+ %% AccRefs contains the offsets of all references to relocatable symbols in
+ %% the code:
+ AccRefs = fix_relocations(TextRelocs, RelocsDict, MFA),
+ %% Get stack descriptors
+ SDescs = get_sdescs(ObjBin),
+ %% FixedSDescs are the stack descriptors after correcting calls that have
+ %% arguments in the stack
+ FixedSDescs =
+ fix_stack_descriptors(RelocsDict, AccRefs, SDescs, ExposedClosures),
+ Refs = AccRefs ++ FixedSDescs,
+ %% Get binary code from object file
+ BinCode = elf_format:extract_text(ObjBin),
+ %% Remove temp files (if needed)
+ ok = remove_temp_folder(Dir, Options),
+ %% Return the code together with information that will be used in the
+ %% hipe_llvm_merge module to produce the final binary that will be loaded
+ %% by the hipe unified loader.
+ {MFA, BinCode, byte_size(BinCode), ConstTab, Refs, LabelMap}.
+
+%%------------------------------------------------------------------------------
+%% LLVM tool chain
+%%------------------------------------------------------------------------------
+
+%% @doc Compile function FunName/Arity to LLVM. Return Dir (in order to remove
+%% it if we do not want to store temporary files) and ObjectFile name that
+%% is created by the LLVM tools.
+compile_with_llvm(FunName, Arity, LLVMCode, Options, UseBuffer) ->
+ Filename = atom_to_list(FunName) ++ "_" ++ integer_to_list(Arity),
+ %% Save temp files in a unique folder
+ Dir = unique_folder(FunName, Arity, Options),
+ ok = file:make_dir(Dir),
+ %% Print LLVM assembly to file
+ OpenOpts = [append, raw] ++
+ case UseBuffer of
+ %% true -> [delayed_write]; % Use delayed_write!
+ false -> []
+ end,
+ {ok, File_llvm} = file:open(Dir ++ Filename ++ ".ll", OpenOpts),
+ hipe_llvm:pp_ins_list(File_llvm, LLVMCode),
+ %% delayed_write can cause file:close not to do a close, hence the two calls
+ ok = file:close(File_llvm),
+ __ = file:close(File_llvm),
+ %% Invoke LLVM compiler tools to produce an object file
+ llvm_opt(Dir, Filename, Options),
+ llvm_llc(Dir, Filename, Options),
+ compile(Dir, Filename, "gcc"), %%FIXME: use llc -filetype=obj and skip this!
+ {ok, Dir, Dir ++ Filename ++ ".o"}.
+
+%% @doc Invoke opt tool to optimize the bitcode (_name.ll -> _name.bc).
+llvm_opt(Dir, Filename, Options) ->
+ Source = Dir ++ Filename ++ ".ll",
+ Dest = Dir ++ Filename ++ ".bc",
+ OptLevel = trans_optlev_flag(opt, Options),
+ OptFlags = [OptLevel, "-mem2reg", "-strip"],
+ Command = "opt " ++ fix_opts(OptFlags) ++ " " ++ Source ++ " -o " ++ Dest,
+ %% io:format("OPT: ~s~n", [Command]),
+ case os:cmd(Command) of
+ "" -> ok;
+ Error -> exit({?MODULE, opt, Error})
+ end.
+
+%% @doc Invoke llc tool to compile the bitcode to object file
+%% (_name.bc -> _name.o).
+llvm_llc(Dir, Filename, Options) ->
+ Source = Dir ++ Filename ++ ".bc",
+ OptLevel = trans_optlev_flag(llc, Options),
+ Align = find_stack_alignment(),
+ LlcFlags = [OptLevel, "-code-model=medium", "-stack-alignment=" ++ Align
+ , "-tailcallopt", "-filetype=asm"], %%FIXME
+ Command = "llc " ++ fix_opts(LlcFlags) ++ " " ++ Source,
+ %% io:format("LLC: ~s~n", [Command]),
+ case os:cmd(Command) of
+ "" -> ok;
+ Error -> exit({?MODULE, llc, Error})
+ end.
+
+%% @doc Invoke the compiler tool ("gcc", "llvmc", etc.) to generate an object
+%% file from native assembly.
+compile(Dir, Fun_Name, Compiler) ->
+ Source = Dir ++ Fun_Name ++ ".s",
+ Dest = Dir ++ Fun_Name ++ ".o",
+ Command = Compiler ++ " -c " ++ Source ++ " -o " ++ Dest,
+ %% io:format("~s: ~s~n", [Compiler, Command]),
+ case os:cmd(Command) of
+ "" -> ok;
+ Error -> exit({?MODULE, cc, Error})
+ end.
+
+find_stack_alignment() ->
+ case get(hipe_target_arch) of
+ x86 -> "4";
+ amd64 -> "8";
+ _ -> exit({?MODULE, find_stack_alignment, "Unimplemented architecture"})
+ end.
+
+%% @doc Join options.
+fix_opts(Opts) ->
+ string:join(Opts, " ").
+
+%% @doc Translate optimization-level flag (default is "O2").
+trans_optlev_flag(Tool, Options) ->
+ Flag = case Tool of
+ opt -> llvm_opt;
+ llc -> llvm_llc
+ end,
+ case proplists:get_value(Flag, Options) of
+ o0 -> ""; % "-O0" does not exist in opt tool
+ o1 -> "-O1";
+ o2 -> "-O2";
+ o3 -> "-O3";
+ undefined -> "-O2"
+ end.
+
+%%------------------------------------------------------------------------------
+%% Functions to manage Relocations
+%%------------------------------------------------------------------------------
+
+%% @doc Get switch table and closure table.
+get_tables(Elf) ->
+ %% Search Symbol Table for an entry with name prefixed with "table_":
+ Triples = elf_format:get_tab_entries(Elf),
+ Switches = [T || T={"table_" ++ _, _, _} <- Triples],
+ Closures = [T || T={"table_closures" ++ _, _, _} <- Switches],
+ {Switches, Closures}.
+
+%% @doc This function associates symbols who point to some table of labels with
+%% the corresponding offsets of the labels in the code. These tables can
+%% either be jump tables for switches or a table which contains the labels
+%% of blocks that contain closure calls with more than ?NR_ARG_REGS.
+correlate_labels([], _L) -> {[], []};
+correlate_labels(Tables, Labels) ->
+ %% Sort "Tables" based on "ValueOffsets"
+ OffsetSortedTb = lists:ukeysort(2, Tables),
+ %% Unzip offset-sorted list of "Switches"
+ {Names, _Offsets, TablesSizeList} = lists:unzip3(OffsetSortedTb),
+ %% Associate switch names with labels
+ L = split_list(Labels, TablesSizeList),
+ %% Zip back! (to [{SwitchName, Values}])
+ NamesValues = lists:zip(Names, L),
+ case lists:keytake("table_closures", 1, NamesValues) of
+ false -> %% No closures in the code, no closure table
+ {NamesValues, []};
+ {value, ClosureTableNV, SwitchesNV} ->
+ {SwitchesNV, ClosureTableNV}
+ end.
+
+%% @doc Create a gb_tree which contains information about the labels that used
+%% for switch's jump tables. The keys of the gb_tree are of the form
+%% {MFA, Label} and the values are the actual Offsets.
+create_labelmap(MFA, SwitchInfos, RelocsDict) ->
+ create_labelmap(MFA, SwitchInfos, RelocsDict, gb_trees:empty()).
+
+create_labelmap(_, [], _, LabelMap) -> LabelMap;
+create_labelmap(MFA, [{Name, Offsets} | Rest], RelocsDict, LabelMap) ->
+ case dict:fetch(Name, RelocsDict) of
+ {switch, {_TableType, LabelList, _NrLabels, _SortOrder}, _JTabLab} ->
+ KVDict = lists:ukeysort(1, lists:zip(LabelList, Offsets)),
+ NewLabelMap = insert_to_labelmap(KVDict, LabelMap),
+ create_labelmap(MFA, Rest, RelocsDict, NewLabelMap);
+ _ ->
+ exit({?MODULE, create_labelmap, "Not a jump table!"})
+ end.
+
+%% @doc Insert a list of [{Key,Value}] to a LabelMap (gb_tree).
+insert_to_labelmap([], LabelMap) -> LabelMap;
+insert_to_labelmap([{Key, Value}|Rest], LabelMap) ->
+ case gb_trees:lookup(Key, LabelMap) of
+ none ->
+ insert_to_labelmap(Rest, gb_trees:insert(Key, Value, LabelMap));
+ {value, Value} -> %% Exists with the *exact* same Value.
+ insert_to_labelmap(Rest, LabelMap)
+ end.
+
+%% @doc Correlate object file relocation symbols with info from translation to
+%% llvm code.
+fix_relocations(Relocs, RelocsDict, MFA) ->
+ fix_relocs(Relocs, RelocsDict, MFA, []).
+
+fix_relocs([], _, _, RelocAcc) -> RelocAcc;
+fix_relocs([{Name, Offset}|Rs], RelocsDict, {ModName,_,_}=MFA, RelocAcc) ->
+ case dict:fetch(Name, RelocsDict) of
+ {atom, AtomName} ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?LOAD_ATOM, Offset, AtomName}|RelocAcc]);
+ {constant, Label} ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?LOAD_ADDRESS, Offset, {constant, Label}}|RelocAcc]);
+ {switch, _, JTabLab} -> %% Treat switch exactly as constant
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?LOAD_ADDRESS, Offset, {constant, JTabLab}}|RelocAcc]);
+ {closure, _}=Closure ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?LOAD_ADDRESS, Offset, Closure}|RelocAcc]);
+ {call, {bif, BifName, _}} ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?CALL_LOCAL, Offset, BifName}|RelocAcc]);
+ %% MFA calls to functions in the same module are of type 3, while all
+ %% other MFA calls are of type 2.
+ {call, {ModName,_F,_A}=CallMFA} ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?CALL_LOCAL, Offset, CallMFA}|RelocAcc]);
+ {call, CallMFA} ->
+ fix_relocs(Rs, RelocsDict, MFA,
+ [{?CALL_REMOTE, Offset, CallMFA}|RelocAcc]);
+ Other ->
+ exit({?MODULE, fix_relocs,
+ {"Relocation not in relocation dictionary", Other}})
+ end.
+
+%%------------------------------------------------------------------------------
+%% Functions to manage Stack Descriptors
+%%------------------------------------------------------------------------------
+
+%% @doc This function takes an ELF Object File binary and returns a proper sdesc
+%% list for Erlang/OTP System's loader. The return value should be of the
+%% form:
+%% {
+%% 4, Safepoint Address,
+%% {ExnLabel OR [], FrameSize, StackArity, {Liveroot stack frame indexes}},
+%% }
+get_sdescs(Elf) ->
+ case elf_format:extract_note(Elf, ?NOTE_ERLGC_NAME) of
+ <<>> -> % Object file has no ".note.gc" section!
+ [];
+ NoteGC_bin ->
+ %% Get safe point addresses (stored in ".rela.note.gc" section):
+ RelaNoteGC = elf_format:extract_rela(Elf, ?NOTE(?NOTE_ERLGC_NAME)),
+ SPCount = length(RelaNoteGC),
+ T = SPCount * ?SP_ADDR_SIZE,
+ %% Pattern match fields of ".note.gc":
+ <<SPCount:(?bits(?SP_COUNT_SIZE))/integer-little, % Sanity check!
+ SPAddrs:T/binary, % NOTE: In 64bit they are relocs!
+ StkFrameSize:(?bits(?SP_STKFRAME_SIZE))/integer-little,
+ StkArity:(?bits(?SP_STKARITY_SIZE))/integer-little,
+ _LiveRootCount:(?bits(?SP_LIVEROOTCNT_SIZE))/integer-little, % Skip
+ Roots/binary>> = NoteGC_bin,
+ LiveRoots = get_liveroots(Roots, []),
+ %% Extract information about the safe point addresses:
+ SPOffs =
+ case elf_format:is64bit() of
+ true -> %% Find offsets in ".rela.note.gc":
+ elf_format:get_rela_addends(RelaNoteGC);
+ false -> %% Find offsets in SPAddrs (in ".note.gc"):
+ get_spoffs(SPAddrs, [])
+ end,
+ %% Extract Exception Handler labels:
+ ExnHandlers = elf_format:get_exn_handlers(Elf),
+ %% Combine ExnHandlers and Safe point addresses (return addresses):
+ ExnAndSPOffs = combine_ras_and_exns(ExnHandlers, SPOffs, []),
+ create_sdesc_list(ExnAndSPOffs, StkFrameSize, StkArity, LiveRoots, [])
+ end.
+
+%% @doc Extracts a bunch of integers (live roots) from a binary. Returns a tuple
+%% as need for stack descriptors.
+get_liveroots(<<>>, Acc) ->
+ list_to_tuple(Acc);
+get_liveroots(<<Root:?bits(?LR_STKINDEX_SIZE)/integer-little,
+ MoreRoots/binary>>, Acc) ->
+ get_liveroots(MoreRoots, [Root | Acc]).
+
+%% @doc Extracts a bunch of integers (safepoint offsets) from a binary. Returns
+%% a tuple as need for stack descriptors.
+get_spoffs(<<>>, Acc) ->
+ lists:reverse(Acc);
+get_spoffs(<<SPOff:?bits(?SP_ADDR_SIZE)/integer-little, More/binary>>, Acc) ->
+ get_spoffs(More, [SPOff | Acc]).
+
+combine_ras_and_exns(_, [], Acc) ->
+ lists:reverse(Acc);
+combine_ras_and_exns(ExnHandlers, [RA | MoreRAs], Acc) ->
+ %% FIXME: do something better than O(n^2) by taking advantage of the property
+ %% ||ExnHandlers|| <= ||RAs||
+ Handler = find_exn_handler(RA, ExnHandlers),
+ combine_ras_and_exns(ExnHandlers, MoreRAs, [{Handler, RA} | Acc]).
+
+find_exn_handler(_, []) ->
+ [];
+find_exn_handler(RA, [{Start, End, Handler} | MoreExnHandlers]) ->
+ case (RA >= Start andalso RA =< End) of
+ true ->
+ Handler;
+ false ->
+ find_exn_handler(RA, MoreExnHandlers)
+ end.
+
+create_sdesc_list([], _, _, _, Acc) ->
+ lists:reverse(Acc);
+create_sdesc_list([{ExnLbl, SPOff} | MoreExnAndSPOffs],
+ StkFrameSize, StkArity, LiveRoots, Acc) ->
+ Hdlr = case ExnLbl of
+ 0 -> [];
+ N -> N
+ end,
+ create_sdesc_list(MoreExnAndSPOffs, StkFrameSize, StkArity, LiveRoots,
+ [{?SDESC, SPOff, {Hdlr, StkFrameSize, StkArity, LiveRoots}}
+ | Acc]).
+
+%% @doc This function is responsible for correcting the stack descriptors of
+%% the calls that are found in the code and have more than NR_ARG_REGS
+%% (thus, some of their arguments are passed to the stack). Because of the
+%% Reserved Call Frame feature that the LLVM uses, the stack descriptors
+%% are not correct since at the point of call the frame size is reduced
+%% by the number of arguments that are passed on the stack. Also, the
+%% offsets of the roots need to be re-adjusted.
+fix_stack_descriptors(_, _, [], _) ->
+ [];
+fix_stack_descriptors(RelocsDict, Relocs, SDescs, ExposedClosures) ->
+ %% NamedCalls are MFA and BIF calls that need fix
+ NamedCalls = calls_with_stack_args(RelocsDict),
+ NamedCallsOffs = calls_offsets_arity(Relocs, NamedCalls),
+ ExposedClosures1 =
+ case dict:is_key("table_closures", RelocsDict) of
+ true -> %% A Table with closures exists
+ {table_closures, ArityList} = dict:fetch("table_closures", RelocsDict),
+ case ExposedClosures of
+ {_, Offsets} ->
+ lists:zip(Offsets, ArityList);
+ _ ->
+ exit({?MODULE, fix_stack_descriptors,
+ {"Wrong exposed closures", ExposedClosures}})
+ end;
+ false ->
+ []
+ end,
+ ClosuresOffs = closures_offsets_arity(ExposedClosures1, SDescs),
+ fix_sdescs(NamedCallsOffs ++ ClosuresOffs, SDescs).
+
+%% @doc This function takes as argument the relocation dictionary as produced by
+%% the translation of RTL code to LLVM and finds the names of the calls
+%% (MFA and BIF calls) that have more than NR_ARG_REGS.
+calls_with_stack_args(Dict) ->
+ calls_with_stack_args(dict:to_list(Dict), []).
+
+calls_with_stack_args([], Calls) -> Calls;
+calls_with_stack_args([ {_Name, {call, {M, F, A}}} | Rest], Calls)
+ when A > ?NR_ARG_REGS ->
+ Call =
+ case M of
+ bif -> {F,A};
+ _ -> {M,F,A}
+ end,
+ calls_with_stack_args(Rest, [Call|Calls]);
+calls_with_stack_args([_|Rest], Calls) ->
+ calls_with_stack_args(Rest, Calls).
+
+%% @doc This function extracts the stack arity and the offset in the code of
+%% the named calls (MFAs, BIFs) that have stack arguments.
+calls_offsets_arity(AccRefs, CallsWithStackArgs) ->
+ calls_offsets_arity(AccRefs, CallsWithStackArgs, []).
+
+calls_offsets_arity([], _, Acc) -> Acc;
+calls_offsets_arity([{Type, Offset, Term} | Rest], CallsWithStackArgs, Acc)
+ when Type =:= ?CALL_REMOTE orelse Type =:= ?CALL_LOCAL ->
+ case lists:member(Term, CallsWithStackArgs) of
+ true ->
+ Arity =
+ case Term of
+ {_M, _F, A} -> A;
+ {_F, A} -> A
+ end,
+ calls_offsets_arity(Rest, CallsWithStackArgs,
+ [{Offset + 4, Arity - ?NR_ARG_REGS} | Acc]);
+ false ->
+ calls_offsets_arity(Rest, CallsWithStackArgs, Acc)
+ end;
+calls_offsets_arity([_|Rest], CallsWithStackArgs, Acc) ->
+ calls_offsets_arity(Rest, CallsWithStackArgs, Acc).
+
+%% @doc This function extracts the stack arity and the offsets of closures that
+%% have stack arity. The Closures argument represents the
+%% hipe_bifs:llvm_exposure_closure/0 calls in the code. The actual closure
+%% is the next call in the code, so the offset of the next call must be
+%% calculated from the stack descriptors.
+closures_offsets_arity([], _) ->
+ [];
+closures_offsets_arity(ExposedClosures, SDescs) ->
+ Offsets = [Offset || {_, Offset, _} <- SDescs],
+ %% Offsets and closures must be sorted in order for find_offsets/3 to work
+ SortedOffsets = lists:sort(Offsets),
+ SortedExposedClosures = lists:keysort(1, ExposedClosures),
+ find_offsets(SortedExposedClosures, SortedOffsets, []).
+
+find_offsets([], _, Acc) -> Acc;
+find_offsets([{Off,Arity}|Rest], Offsets, Acc) ->
+ [I | RestOffsets] = lists:dropwhile(fun (Y) -> Y<Off end, Offsets),
+ find_offsets(Rest, RestOffsets, [{I, Arity}|Acc]).
+
+%% The function below corrects the stack descriptors of calls with arguments
+%% that are passed on the stack (more than NR_ARG_REGS) by subtracting the
+%% number of stacked arguments from the frame size and from the offset of the
+%% roots.
+fix_sdescs([], SDescs) -> SDescs;
+fix_sdescs([{Offset, Arity} | Rest], SDescs) ->
+ case lists:keyfind(Offset, 2, SDescs) of
+ false ->
+ fix_sdescs(Rest, SDescs);
+ {?SDESC, Offset, {ExnHandler, FrameSize, StkArity, Roots}} ->
+ FixedRoots = list_to_tuple([Ri - Arity || Ri <- tuple_to_list(Roots)]),
+ FixedSDesc =
+ {?SDESC, Offset, {ExnHandler, FrameSize - Arity, StkArity, FixedRoots}},
+ fix_sdescs(Rest, [FixedSDesc | lists:keydelete(Offset, 2, SDescs)])
+ end.
+
+%%------------------------------------------------------------------------------
+%% Miscellaneous functions
+%%------------------------------------------------------------------------------
+
+%% @doc A function that opens a file as binary. The function takes as argument
+%% the name of the file and returns an Erlang binary.
+-spec open_object_file(string()) -> binary().
+open_object_file(ObjFile) ->
+ case file:read_file(ObjFile) of
+ {ok, Binary} ->
+ Binary;
+ {error, Reason} ->
+ exit({?MODULE, open_file, Reason})
+ end.
+
+remove_temp_folder(Dir, Options) ->
+ case proplists:get_bool(llvm_save_temps, Options) of
+ true -> ok;
+ false -> spawn(fun () -> "" = os:cmd("rm -rf " ++ Dir) end), ok
+ end.
+
+unique_id(FunName, Arity) ->
+ integer_to_list(erlang:phash2({FunName, Arity, now()})).
+
+unique_folder(FunName, Arity, Options) ->
+ DirName = "llvm_" ++ unique_id(FunName, Arity) ++ "/",
+ Dir =
+ case proplists:get_bool(llvm_save_temps, Options) of
+ true -> %% Store folder in current directory
+ DirName;
+ false -> %% Temporarily store folder in tempfs (/dev/shm/)
+ "/dev/shm/" ++ DirName
+ end,
+ %% Make sure it does not exist
+ case dir_exists(Dir) of
+ true -> %% Dir already exists! Generate again.
+ unique_folder(FunName, Arity, Options);
+ false ->
+ Dir
+ end.
+
+%% @doc Function that checks that a given Filename is an existing Directory
+%% Name (from http://rosettacode.org/wiki/Ensure_that_a_file_exists#Erlang)
+dir_exists(Filename) ->
+ {Flag, Info} = file:read_file_info(Filename),
+ (Flag =:= ok) andalso (element(3, Info) =:= directory).
+
+%% @doc Function that takes as arguments a list of integers and a list with
+%% numbers indicating how many items should each tuple have and splits
+%% the original list to a list of lists of integers (with the specified
+%% number of elements), i.e. [ [...], [...] ].
+-spec split_list([integer()], [integer()]) -> [ [integer()] ].
+split_list(List, ElemsPerTuple) ->
+ split_list(List, ElemsPerTuple, []).
+
+-spec split_list([integer()], [integer()], [ [integer()] ]) -> [ [integer()] ].
+split_list([], [], Acc) ->
+ lists:reverse(Acc);
+split_list(List, [NumOfElems | MoreNums], Acc) ->
+ {L1, L2} = lists:split(NumOfElems, List),
+ split_list(L2, MoreNums, [ L1 | Acc]).
diff --git a/lib/hipe/llvm/hipe_llvm_merge.erl b/lib/hipe/llvm/hipe_llvm_merge.erl
new file mode 100644
index 0000000000..3ababfc21a
--- /dev/null
+++ b/lib/hipe/llvm/hipe_llvm_merge.erl
@@ -0,0 +1,114 @@
+%%% -*- erlang-indent-level: 2 -*-
+-module(hipe_llvm_merge).
+
+-export([finalize/3]).
+
+-include("hipe_llvm_arch.hrl").
+-include("../../kernel/src/hipe_ext_format.hrl").
+-include("../rtl/hipe_literals.hrl").
+-include("../main/hipe.hrl").
+
+finalize(CompiledCode, Closures, Exports) ->
+ CompiledCode1 = [CodePack || {_, CodePack} <- CompiledCode],
+ Code = [{MFA, [], ConstTab}
+ || {MFA, _, _ , ConstTab, _, _} <- CompiledCode1],
+ {ConstAlign, ConstSize, ConstMap, RefsFromConsts} =
+ hipe_pack_constants:pack_constants(Code, ?ARCH_REGISTERS:alignment()),
+ %% Compute total code size separately as a sanity check for alignment
+ CodeSize = compute_code_size(CompiledCode1, 0),
+ %% io:format("Code Size (pre-computed): ~w~n", [CodeSize]),
+ {CodeBinary, ExportMap} = merge_mfas(CompiledCode1, 0, <<>>, []),
+ %% io:format("Code Size (post-computed): ~w~n", [byte_size(CodeBinary)]),
+ ?VERBOSE_ASSERT(CodeSize =:= byte_size(CodeBinary)),
+ AccRefs = merge_refs(CompiledCode1, ConstMap, 0, []),
+ %% Bring CompiledCode to a combine_label_maps-acceptable form.
+ LabelMap = combine_label_maps(CompiledCode1, 0, gb_trees:empty()),
+ SC = hipe_pack_constants:slim_constmap(ConstMap),
+ DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
+ SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap, Closures, Exports),
+ SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
+ term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC},
+ ConstAlign, ConstSize,
+ SC, % ConstMap
+ DataRelocs, % LabelMap
+ SSE, % ExportMap
+ CodeSize, CodeBinary, SlimRefs,
+ 0,[] % ColdCodeSize, SlimColdRefs
+ ]).
+
+%% Copied from hipe_x86_assemble.erl
+nr_pad_bytes(Address) ->
+ (4 - (Address rem 4)) rem 4. % XXX: 16 or 32 instead?
+
+align_entry(Address) ->
+ Address + nr_pad_bytes(Address).
+
+compute_code_size([{_MFA, _BinaryCode, CodeSize, _, _, _}|Code], Size) ->
+ compute_code_size(Code, align_entry(Size+CodeSize));
+compute_code_size([], Size) -> Size.
+
+combine_label_maps([{MFA, _, CodeSize, _, _, LabelMap}|Code], Address, CLM) ->
+ NewCLM = merge_label_map(gb_trees:to_list(LabelMap), MFA, Address, CLM),
+ combine_label_maps(Code, align_entry(Address+CodeSize), NewCLM);
+combine_label_maps([], _Address, CLM) -> CLM.
+
+merge_label_map([{Label,Offset}|Rest], MFA, Address, CLM) ->
+ NewCLM = gb_trees:insert({MFA,Label}, Address+Offset, CLM),
+ merge_label_map(Rest, MFA, Address, NewCLM);
+merge_label_map([], _MFA, _Address, CLM) -> CLM.
+
+%% @doc Merge the MFAs' binary code to one continuous binary and compute the
+%% size of this binary. At the same time create an exportmap in a form
+%% of {Address, M, F, A}.
+%% XXX: Is alignment correct/optimal for X86/AMD64?
+merge_mfas([{{M,F,A}, CodeBinary, CodeSize, _, _, _}|Code],
+ Address, AccCode, AccExportMap) ->
+ ?VERBOSE_ASSERT(CodeSize =:= byte_size(CodeBinary)),
+ {Address1, Code1} =
+ case nr_pad_bytes(Address + CodeSize) of
+ 0 -> %% Retains alignment:
+ {Address + CodeSize, CodeBinary};
+ NrPadBytes -> %% Needs padding!
+ Padding = list_to_binary(lists:duplicate(NrPadBytes, 0)),
+ {Address + CodeSize + NrPadBytes, % =:= align_entry(Address+CodeSize)
+ <<CodeBinary/binary, Padding/binary>>}
+ end,
+ ?VERBOSE_ASSERT(Address1 =:=
+ align_entry(Address + CodeSize)), %XXX: Should address be aligned?
+ AccCode1 = <<AccCode/binary, Code1/binary>>,
+ merge_mfas(Code, Address1, AccCode1, [{Address, M, F, A}|AccExportMap]);
+merge_mfas([], _Address, AccCode, AccExportMap) ->
+ {AccCode, AccExportMap}.
+
+%% @doc Merge the references of relocatable symbols in the binary code. The
+%% offsets must be updated because of the merging of the code binaries!
+merge_refs([], _ConstMap, _Addr, AccRefs) -> AccRefs;
+merge_refs([{MFA, _, CodeSize, _, Refs, _}|Rest], ConstMap, Address, AccRefs) ->
+ %% Important!: The hipe_pack_constants:pack_constants/2 function assignes
+ %% unique numbers to constants (ConstNo). This numbers are used from now on,
+ %% instead of labels that were used before. So, in order to be compatible, we
+ %% must change all the constant labels in the Refs to the corresponding
+ %% ConstNo, that can be found in the ConstMap (#pcm_entry{}).
+ UpdatedRefs = [update_ref(label_to_constno(Ref, MFA, ConstMap), Address)
+ || Ref <- Refs],
+ merge_refs(Rest, ConstMap, align_entry(Address+CodeSize),
+ UpdatedRefs++AccRefs).
+
+label_to_constno({Type, Offset, {constant, Label}}, MFA, ConstMap) ->
+ ConstNo = hipe_pack_constants:find_const({MFA, Label}, ConstMap),
+ {Type, Offset, {constant, ConstNo}};
+label_to_constno(Other, _MFA, _ConstMap) ->
+ Other.
+
+%% @doc Update offset to a reference. In case of stack descriptors we must check
+%% if there exists an exception handler, because it must also be updated.
+update_ref({?SDESC, Offset, SDesc}, CodeAddr) ->
+ NewRefAddr = Offset+CodeAddr,
+ case SDesc of
+ {[], _, _, _} -> % No handler; only update offset
+ {?SDESC, NewRefAddr, SDesc};
+ {ExnHandler, FrameSize, StackArity, Roots} -> % Update exception handler
+ {?SDESC, NewRefAddr, {ExnHandler+CodeAddr, FrameSize, StackArity, Roots}}
+ end;
+update_ref({Type, Offset, Term}, CodeAddr) ->
+ {Type, Offset+CodeAddr, Term}.
diff --git a/lib/hipe/llvm/hipe_rtl_to_llvm.erl b/lib/hipe/llvm/hipe_rtl_to_llvm.erl
new file mode 100644
index 0000000000..d7d8d1b049
--- /dev/null
+++ b/lib/hipe/llvm/hipe_rtl_to_llvm.erl
@@ -0,0 +1,1613 @@
+%% -*- erlang-indent-level: 2 -*-
+
+-module(hipe_rtl_to_llvm).
+-author("Chris Stavrakakis, Yiannis Tsiouris").
+
+-export([translate/2]). % the main function of this module
+-export([fix_mfa_name/1]). % a help function used in hipe_llvm_main
+
+-include("../rtl/hipe_rtl.hrl").
+-include("../rtl/hipe_literals.hrl").
+-include("hipe_llvm_arch.hrl").
+
+-define(WORD_WIDTH, (?bytes_to_bits(hipe_rtl_arch:word_size()))).
+-define(BRANCH_META_TAKEN, "0").
+-define(BRANCH_META_NOT_TAKEN, "1").
+
+%%------------------------------------------------------------------------------
+%% @doc Main function for translating an RTL function to LLVM Assembly. Takes as
+%% input the RTL code and the variable indexes of possible garbage
+%% collection roots and returns the corresponing LLVM, a dictionary with
+%% all the relocations in the code and a hipe_consttab() with informaton
+%% about data.
+%%------------------------------------------------------------------------------
+translate(RTL, Roots) ->
+ Fun = hipe_rtl:rtl_fun(RTL),
+ Params = hipe_rtl:rtl_params(RTL),
+ Data = hipe_rtl:rtl_data(RTL),
+ Code = hipe_rtl:rtl_code(RTL),
+ %% Init unique symbol generator and initialize the label counter to the last
+ %% RTL label.
+ hipe_gensym:init(llvm),
+ {_, MaxLabel} = hipe_rtl:rtl_label_range(RTL),
+ put({llvm,label_count}, MaxLabel + 1),
+ %% Put first label of RTL code in process dictionary
+ find_code_entry_label(Code),
+ %% Initialize relocations symbol dictionary
+ Relocs = dict:new(),
+ %% Print RTL to file
+ %% {ok, File_rtl} = file:open("rtl_" ++integer_to_list(random:uniform(2000))
+ %% ++ ".rtl", [write]),
+ %% hipe_rtl:pp(File_rtl, RTL),
+ %% file:close(File_rtl),
+
+ %% Pass on RTL code to handle exception handling and identify labels of Fail
+ %% Blocks
+ {Code1, FailLabels} = fix_code(Code),
+ %% Allocate stack slots for each virtual register and declare gc roots
+ AllocaStackCode = alloca_stack(Code1, Params, Roots),
+ %% Translate Code
+ {LLVM_Code1, Relocs1, NewData} =
+ translate_instr_list(Code1, [], Relocs, Data),
+ %% Create LLVM code to declare relocation symbols as external symbols along
+ %% with local variables in order to use them as just any other variable
+ {FinalRelocs, ExternalDecl, LocalVars} =
+ handle_relocations(Relocs1, Data, Fun),
+ %% Pass on LLVM code in order to create Fail blocks and a landingpad
+ %% instruction to each one
+ LLVM_Code2 = add_landingpads(LLVM_Code1, FailLabels),
+ %% Create LLVM Code for the compiled function
+ LLVM_Code3 = create_function_definition(Fun, Params, LLVM_Code2,
+ AllocaStackCode ++ LocalVars),
+ %% Final Code = CompiledFunction + External Declarations
+ FinalLLVMCode = [LLVM_Code3 | ExternalDecl],
+ {FinalLLVMCode, FinalRelocs, NewData}.
+
+find_code_entry_label([]) ->
+ exit({?MODULE, find_code_entry_label, "Empty code"});
+find_code_entry_label([I|_]) ->
+ case hipe_rtl:is_label(I) of
+ true ->
+ put(first_label, hipe_rtl:label_name(I));
+ false ->
+ exit({?MODULE, find_code_entry_label, "First instruction is not a label"})
+ end.
+
+%% @doc Create a stack slot for each virtual register. The stack slots
+%% that correspond to possible garbage collection roots must be
+%% marked as such.
+alloca_stack(Code, Params, Roots) ->
+ %% Find all assigned virtual registers
+ Destinations = collect_destinations(Code),
+ %% Declare virtual registers, and declare garbage collection roots
+ do_alloca_stack(Destinations++Params, Params, Roots).
+
+collect_destinations(Code) ->
+ lists:usort(lists:flatmap(fun insn_dst/1, Code)).
+
+do_alloca_stack(Destinations, Params, Roots) ->
+ do_alloca_stack(Destinations, Params, Roots, []).
+
+do_alloca_stack([], _, _, Acc) ->
+ Acc;
+do_alloca_stack([D|Ds], Params, Roots, Acc) ->
+ {Name, _I} = trans_dst(D),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ case hipe_rtl:is_var(D) of
+ true ->
+ Num = hipe_rtl:var_index(D),
+ I1 = hipe_llvm:mk_alloca(Name, WordTy, [], []),
+ case lists:member(Num, Roots) of
+ true -> %% Variable is a possible Root
+ T1 = mk_temp(),
+ BYTE_TYPE_PP = hipe_llvm:mk_pointer(ByteTyPtr),
+ I2 =
+ hipe_llvm:mk_conversion(T1, bitcast, WordTyPtr, Name, BYTE_TYPE_PP),
+ GcRootArgs = [{BYTE_TYPE_PP, T1}, {ByteTyPtr, "@gc_metadata"}],
+ I3 = hipe_llvm:mk_call([], false, [], [], hipe_llvm:mk_void(),
+ "@llvm.gcroot", GcRootArgs, []),
+ I4 = case lists:member(D, Params) of
+ false ->
+ hipe_llvm:mk_store(WordTy, "-5", WordTyPtr, Name,
+ [], [], false);
+ true -> []
+ end,
+ do_alloca_stack(Ds, Params, Roots, [I1, I2, I3, I4 | Acc]);
+ false ->
+ do_alloca_stack(Ds, Params, Roots, [I1|Acc])
+ end;
+ false ->
+ case hipe_rtl:is_reg(D) andalso isPrecoloured(D) of
+ true -> %% Precoloured registers are mapped to "special" stack slots
+ do_alloca_stack(Ds, Params, Roots, Acc);
+ false ->
+ I1 = case hipe_rtl:is_fpreg(D) of
+ true ->
+ FloatTy = hipe_llvm:mk_double(),
+ hipe_llvm:mk_alloca(Name, FloatTy, [], []);
+ false -> hipe_llvm:mk_alloca(Name, WordTy, [], [])
+ end,
+ do_alloca_stack(Ds, Params, Roots, [I1|Acc])
+ end
+ end.
+
+%%------------------------------------------------------------------------------
+%% @doc Translation of the linearized RTL Code. Each RTL instruction is
+%% translated to a list of LLVM Assembly instructions. The relocation
+%% dictionary is updated when needed.
+%%------------------------------------------------------------------------------
+translate_instr_list([], Acc, Relocs, Data) ->
+ {lists:reverse(lists:flatten(Acc)), Relocs, Data};
+translate_instr_list([I | Is], Acc, Relocs, Data) ->
+ {Acc1, NewRelocs, NewData} = translate_instr(I, Relocs, Data),
+ translate_instr_list(Is, [Acc1 | Acc], NewRelocs, NewData).
+
+translate_instr(I, Relocs, Data) ->
+ case I of
+ #alu{} ->
+ {I2, Relocs2} = trans_alu(I, Relocs),
+ {I2, Relocs2, Data};
+ #alub{} ->
+ {I2, Relocs2} = trans_alub(I, Relocs),
+ {I2, Relocs2, Data};
+ #branch{} ->
+ {I2, Relocs2} = trans_branch(I, Relocs),
+ {I2, Relocs2, Data};
+ #call{} ->
+ {I2, Relocs2} =
+ case hipe_rtl:call_fun(I) of
+ %% In AMD64 this instruction does nothing!
+ %% TODO: chech use of fwait in other architectures!
+ fwait ->
+ {[], Relocs};
+ _ ->
+ trans_call(I, Relocs)
+ end,
+ {I2, Relocs2, Data};
+ #comment{} ->
+ {I2, Relocs2} = trans_comment(I, Relocs),
+ {I2, Relocs2, Data};
+ #enter{} ->
+ {I2, Relocs2} = trans_enter(I, Relocs),
+ {I2, Relocs2, Data};
+ #fconv{} ->
+ {I2, Relocs2} = trans_fconv(I, Relocs),
+ {I2, Relocs2, Data};
+ #fload{} ->
+ {I2, Relocs2} = trans_fload(I, Relocs),
+ {I2, Relocs2, Data};
+ #fmove{} ->
+ {I2, Relocs2} = trans_fmove(I, Relocs),
+ {I2, Relocs2, Data};
+ #fp{} ->
+ {I2, Relocs2} = trans_fp(I, Relocs),
+ {I2, Relocs2, Data};
+ #fp_unop{} ->
+ {I2, Relocs2} = trans_fp_unop(I, Relocs),
+ {I2, Relocs2, Data};
+ #fstore{} ->
+ {I2, Relocs2} = trans_fstore(I, Relocs),
+ {I2, Relocs2, Data};
+ #goto{} ->
+ {I2, Relocs2} = trans_goto(I, Relocs),
+ {I2, Relocs2, Data};
+ #label{} ->
+ {I2, Relocs2} = trans_label(I, Relocs),
+ {I2, Relocs2, Data};
+ #load{} ->
+ {I2, Relocs2} = trans_load(I, Relocs),
+ {I2, Relocs2, Data};
+ #load_address{} ->
+ {I2, Relocs2} = trans_load_address(I, Relocs),
+ {I2, Relocs2, Data};
+ #load_atom{} ->
+ {I2, Relocs2} = trans_load_atom(I, Relocs),
+ {I2, Relocs2, Data};
+ #move{} ->
+ {I2, Relocs2} = trans_move(I, Relocs),
+ {I2, Relocs2, Data};
+ #return{} ->
+ {I2, Relocs2} = trans_return(I, Relocs),
+ {I2, Relocs2, Data};
+ #store{} ->
+ {I2, Relocs2} = trans_store(I, Relocs),
+ {I2, Relocs2, Data};
+ #switch{} -> %% Only switch instruction updates Data
+ {I2, Relocs2, NewData} = trans_switch(I, Relocs, Data),
+ {I2, Relocs2, NewData};
+ Other ->
+ exit({?MODULE, translate_instr, {"Unknown RTL instruction", Other}})
+ end.
+
+%%
+%% alu
+%%
+trans_alu(I, Relocs) ->
+ RtlDst = hipe_rtl:alu_dst(I),
+ TmpDst = mk_temp(),
+ {Src1, I1} = trans_src(hipe_rtl:alu_src1(I)),
+ {Src2, I2} = trans_src(hipe_rtl:alu_src2(I)),
+ Op = trans_op(hipe_rtl:alu_op(I)),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I3 = hipe_llvm:mk_operation(TmpDst, Op, WordTy, Src1, Src2, []),
+ I4 = store_stack_dst(TmpDst, RtlDst),
+ {[I4, I3, I2, I1], Relocs}.
+
+%%
+%% alub
+%%
+trans_alub(I, Relocs) ->
+ case hipe_rtl:alub_cond(I) of
+ Op when Op =:= overflow orelse Op =:= not_overflow ->
+ trans_alub_overflow(I, signed, Relocs);
+ ltu -> %% ltu means unsigned overflow
+ trans_alub_overflow(I, unsigned, Relocs);
+ _ ->
+ trans_alub_no_overflow(I, Relocs)
+ end.
+
+trans_alub_overflow(I, Sign, Relocs) ->
+ {Src1, I1} = trans_src(hipe_rtl:alub_src1(I)),
+ {Src2, I2} = trans_src(hipe_rtl:alub_src2(I)),
+ RtlDst = hipe_rtl:alub_dst(I),
+ TmpDst = mk_temp(),
+ Name = trans_alub_op(I, Sign),
+ NewRelocs = relocs_store(Name, {call, {llvm, Name, 2}}, Relocs),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ ReturnType = hipe_llvm:mk_struct([WordTy, hipe_llvm:mk_int(1)]),
+ T1 = mk_temp(),
+ I3 = hipe_llvm:mk_call(T1, false, [], [], ReturnType, "@" ++ Name,
+ [{WordTy, Src1}, {WordTy, Src2}], []),
+ %% T1{0}: result of the operation
+ I4 = hipe_llvm:mk_extractvalue(TmpDst, ReturnType, T1 , "0", []),
+ I5 = store_stack_dst(TmpDst, RtlDst),
+ T2 = mk_temp(),
+ %% T1{1}: Boolean variable indicating overflow
+ I6 = hipe_llvm:mk_extractvalue(T2, ReturnType, T1, "1", []),
+ case hipe_rtl:alub_cond(I) of
+ Op when Op =:= overflow orelse Op =:= ltu ->
+ True_label = mk_jump_label(hipe_rtl:alub_true_label(I)),
+ False_label = mk_jump_label(hipe_rtl:alub_false_label(I)),
+ MetaData = branch_metadata(hipe_rtl:alub_pred(I));
+ not_overflow ->
+ True_label = mk_jump_label(hipe_rtl:alub_false_label(I)),
+ False_label = mk_jump_label(hipe_rtl:alub_true_label(I)),
+ MetaData = branch_metadata(1 - hipe_rtl:alub_pred(I))
+ end,
+ I7 = hipe_llvm:mk_br_cond(T2, True_label, False_label, MetaData),
+ {[I7, I6, I5, I4, I3, I2, I1], NewRelocs}.
+
+trans_alub_op(I, Sign) ->
+ Name =
+ case Sign of
+ signed ->
+ case hipe_rtl:alub_op(I) of
+ add -> "llvm.sadd.with.overflow.";
+ mul -> "llvm.smul.with.overflow.";
+ sub -> "llvm.ssub.with.overflow.";
+ Op -> exit({?MODULE, trans_alub_op, {"Unknown alub operator", Op}})
+ end;
+ unsigned ->
+ case hipe_rtl:alub_op(I) of
+ add -> "llvm.uadd.with.overflow.";
+ mul -> "llvm.umul.with.overflow.";
+ sub -> "llvm.usub.with.overflow.";
+ Op -> exit({?MODULE, trans_alub_op, {"Unknown alub operator", Op}})
+ end
+ end,
+ Type =
+ case hipe_rtl_arch:word_size() of
+ 4 -> "i32";
+ 8 -> "i64"
+ %% Other -> exit({?MODULE, trans_alub_op, {"Unknown type", Other}})
+ end,
+ Name ++ Type.
+
+trans_alub_no_overflow(I, Relocs) ->
+ %% alu
+ T = hipe_rtl:mk_alu(hipe_rtl:alub_dst(I), hipe_rtl:alub_src1(I),
+ hipe_rtl:alub_op(I), hipe_rtl:alub_src2(I)),
+ %% A trans_alu instruction cannot change relocations
+ {I1, _} = trans_alu(T, Relocs),
+ %% icmp
+ %% Translate destination as src, to match with the semantics of instruction
+ {Dst, I2} = trans_src(hipe_rtl:alub_dst(I)),
+ Cond = trans_rel_op(hipe_rtl:alub_cond(I)),
+ T3 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I5 = hipe_llvm:mk_icmp(T3, Cond, WordTy, Dst, "0"),
+ %% br
+ Metadata = branch_metadata(hipe_rtl:alub_pred(I)),
+ True_label = mk_jump_label(hipe_rtl:alub_true_label(I)),
+ False_label = mk_jump_label(hipe_rtl:alub_false_label(I)),
+ I6 = hipe_llvm:mk_br_cond(T3, True_label, False_label, Metadata),
+ {[I6, I5, I2, I1], Relocs}.
+
+%%
+%% branch
+%%
+trans_branch(I, Relocs) ->
+ {Src1, I1} = trans_src(hipe_rtl:branch_src1(I)),
+ {Src2, I2} = trans_src(hipe_rtl:branch_src2(I)),
+ Cond = trans_rel_op(hipe_rtl:branch_cond(I)),
+ %% icmp
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I3 = hipe_llvm:mk_icmp(T1, Cond, WordTy, Src1, Src2),
+ %% br
+ True_label = mk_jump_label(hipe_rtl:branch_true_label(I)),
+ False_label = mk_jump_label(hipe_rtl:branch_false_label(I)),
+ Metadata = branch_metadata(hipe_rtl:branch_pred(I)),
+ I4 = hipe_llvm:mk_br_cond(T1, True_label, False_label, Metadata),
+ {[I4, I3, I2, I1], Relocs}.
+
+branch_metadata(X) when X =:= 0.5 -> [];
+branch_metadata(X) when X > 0.5 -> ?BRANCH_META_TAKEN;
+branch_metadata(X) when X < 0.5 -> ?BRANCH_META_NOT_TAKEN.
+
+%%
+%% call
+%%
+trans_call(I, Relocs) ->
+ RtlCallArgList= hipe_rtl:call_arglist(I),
+ RtlCallName = hipe_rtl:call_fun(I),
+ {I0, Relocs1} = expose_closure(RtlCallName, RtlCallArgList, Relocs),
+ TmpDst = mk_temp(),
+ {CallArgs, I1} = trans_call_args(RtlCallArgList),
+ FixedRegs = fixed_registers(),
+ {LoadedFixedRegs, I2} = load_fixed_regs(FixedRegs),
+ FinalArgs = fix_reg_args(LoadedFixedRegs) ++ CallArgs,
+ {Name, I3, Relocs2} =
+ trans_call_name(RtlCallName, Relocs1, CallArgs, FinalArgs),
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ I4 =
+ case hipe_rtl:call_fail(I) of
+ %% Normal Call
+ [] ->
+ hipe_llvm:mk_call(T1, false, "cc 11", [], FunRetTy, Name, FinalArgs,
+ []);
+ %% Call With Exception
+ FailLabelNum ->
+ TrueLabel = "L" ++ integer_to_list(hipe_rtl:call_normal(I)),
+ FailLabel = "%FL" ++ integer_to_list(FailLabelNum),
+ II1 =
+ hipe_llvm:mk_invoke(T1, "cc 11", [], FunRetTy, Name, FinalArgs, [],
+ "%" ++ TrueLabel, FailLabel),
+ II2 = hipe_llvm:mk_label(TrueLabel),
+ [II2, II1]
+ end,
+ I5 = store_fixed_regs(FixedRegs, T1),
+ I6 =
+ case hipe_rtl:call_dstlist(I) of
+ [] -> []; %% No return value
+ [Destination] ->
+ II3 =
+ hipe_llvm:mk_extractvalue(TmpDst, FunRetTy, T1,
+ integer_to_list(?NR_PINNED_REGS), []),
+ II4 = store_stack_dst(TmpDst, Destination),
+ [II4, II3]
+ end,
+ I7 =
+ case hipe_rtl:call_continuation(I) of
+ [] -> []; %% No continuation
+ CC ->
+ {II5, _} = trans_goto(hipe_rtl:mk_goto(CC), Relocs2),
+ II5
+ end,
+ {[I7, I6, I5, I4, I3, I2, I1, I0], Relocs2}.
+
+%% In case of call to a register (closure call) with more than ?NR_ARG_REGS
+%% arguments we must track the offset this call in the code, in order to
+%% to correct the stack descriptor. So, we insert a new Label and add this label
+%% to the "table_closures"
+%% --------------------------------|--------------------------------------------
+%% Old Code | New Code
+%% --------------------------------|--------------------------------------------
+%% | br %ClosureLabel
+%% call %reg(Args) | ClosureLabel:
+%% | call %reg(Args)
+expose_closure(CallName, CallArgs, Relocs) ->
+ CallArgsNr = length(CallArgs),
+ case hipe_rtl:is_reg(CallName) andalso CallArgsNr > ?NR_ARG_REGS of
+ true ->
+ LabelNum = hipe_gensym:new_label(llvm),
+ ClosureLabel = hipe_llvm:mk_label(mk_label(LabelNum)),
+ JumpIns = hipe_llvm:mk_br(mk_jump_label(LabelNum)),
+ Relocs1 =
+ relocs_store({CallName, LabelNum},
+ {closure_label, LabelNum, CallArgsNr - ?NR_ARG_REGS},
+ Relocs),
+ {[ClosureLabel, JumpIns], Relocs1};
+ false ->
+ {[], Relocs}
+ end.
+
+trans_call_name(RtlCallName, Relocs, CallArgs, FinalArgs) ->
+ case RtlCallName of
+ PrimOp when is_atom(PrimOp) ->
+ LlvmName = trans_prim_op(PrimOp),
+ Relocs1 =
+ relocs_store(LlvmName, {call, {bif, PrimOp, length(CallArgs)}}, Relocs),
+ {"@" ++ LlvmName, [], Relocs1};
+ {M, F, A} when is_atom(M), is_atom(F), is_integer(A) ->
+ LlvmName = trans_mfa_name({M, F, A}),
+ Relocs1 =
+ relocs_store(LlvmName, {call, {M, F, length(CallArgs)}}, Relocs),
+ {"@" ++ LlvmName, [], Relocs1};
+ Reg ->
+ case hipe_rtl:is_reg(Reg) of
+ true ->
+ %% In case of a closure call, the register holding the address
+ %% of the closure must be converted to function type in
+ %% order to make the call
+ TT1 = mk_temp(),
+ {RegName, II1} = trans_src(Reg),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ II2 =
+ hipe_llvm:mk_conversion(TT1, inttoptr, WordTy, RegName, WordTyPtr),
+ TT2 = mk_temp(),
+ ArgsTypeList = lists:duplicate(length(FinalArgs), WordTy),
+ FunRetTy =
+ hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ FunType = hipe_llvm:mk_fun(FunRetTy, ArgsTypeList),
+ FunTypeP = hipe_llvm:mk_pointer(FunType),
+ II3 = hipe_llvm:mk_conversion(TT2, bitcast, WordTyPtr, TT1, FunTypeP),
+ {TT2, [II3, II2, II1], Relocs};
+ false ->
+ exit({?MODULE, trans_call, {"Unimplemented call to", RtlCallName}})
+ end
+ end.
+
+%%
+trans_call_args(ArgList) ->
+ {Args, I} = lists:unzip(trans_args(ArgList)),
+ %% Reverse arguments that are passed to stack to match with the Erlang
+ %% calling convention. (Propably not needed in prim calls.)
+ ReversedArgs =
+ case erlang:length(Args) > ?NR_ARG_REGS of
+ false ->
+ Args;
+ true ->
+ {ArgsInRegs, ArgsInStack} = lists:split(?NR_ARG_REGS, Args),
+ ArgsInRegs ++ lists:reverse(ArgsInStack)
+ end,
+ %% Reverse I, because some of the arguments may go out of scope and
+ %% should be killed(store -5). When two or more arguments are they
+ %% same, then order matters!
+ {ReversedArgs, lists:reverse(I)}.
+
+%%
+%% trans_comment
+%%
+trans_comment(I, Relocs) ->
+ I1 = hipe_llvm:mk_comment(hipe_rtl:comment_text(I)),
+ {I1, Relocs}.
+
+%%
+%% enter
+%%
+trans_enter(I, Relocs) ->
+ {CallArgs, I0} = trans_call_args(hipe_rtl:enter_arglist(I)),
+ FixedRegs = fixed_registers(),
+ {LoadedFixedRegs, I1} = load_fixed_regs(FixedRegs),
+ FinalArgs = fix_reg_args(LoadedFixedRegs) ++ CallArgs,
+ {Name, I2, NewRelocs} =
+ trans_call_name(hipe_rtl:enter_fun(I), Relocs, CallArgs, FinalArgs),
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ I3 = hipe_llvm:mk_call(T1, true, "cc 11", [], FunRetTy, Name, FinalArgs, []),
+ I4 = hipe_llvm:mk_ret([{FunRetTy, T1}]),
+ {[I4, I3, I2, I1, I0], NewRelocs}.
+
+%%
+%% fconv
+%%
+trans_fconv(I, Relocs) ->
+ %% XXX: Can a fconv destination be a precoloured reg?
+ RtlDst = hipe_rtl:fconv_dst(I),
+ TmpDst = mk_temp(),
+ {Src, I1} = trans_float_src(hipe_rtl:fconv_src(I)),
+ FloatTy = hipe_llvm:mk_double(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I2 = hipe_llvm:mk_conversion(TmpDst, sitofp, WordTy, Src, FloatTy),
+ I3 = store_float_stack(TmpDst, RtlDst),
+ {[I3, I2, I1], Relocs}.
+
+
+%% TODO: fload, fstore, fmove, and fp are almost the same with load, store, move
+%% and alu. Maybe we should join them.
+
+%%
+%% fload
+%%
+trans_fload(I, Relocs) ->
+ RtlDst = hipe_rtl:fload_dst(I),
+ RtlSrc = hipe_rtl:fload_src(I),
+ _Offset = hipe_rtl:fload_offset(I),
+ TmpDst = mk_temp(),
+ {Src, I1} = trans_float_src(RtlSrc),
+ {Offset, I2} = trans_float_src(_Offset),
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FloatTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_double()),
+ I3 = hipe_llvm:mk_operation(T1, add, WordTy, Src, Offset, []),
+ T2 = mk_temp(),
+ I4 = hipe_llvm:mk_conversion(T2, inttoptr, WordTy, T1, FloatTyPtr),
+ I5 = hipe_llvm:mk_load(TmpDst, FloatTyPtr, T2, [], [], false),
+ I6 = store_float_stack(TmpDst, RtlDst),
+ {[I6, I5, I4, I3, I2, I1], Relocs}.
+
+%%
+%% fmove
+%%
+trans_fmove(I, Relocs) ->
+ RtlDst = hipe_rtl:fmove_dst(I),
+ RtlSrc = hipe_rtl:fmove_src(I),
+ {Src, I1} = trans_float_src(RtlSrc),
+ I2 = store_float_stack(Src, RtlDst),
+ {[I2, I1], Relocs}.
+
+%%
+%% fp
+%%
+trans_fp(I, Relocs) ->
+ %% XXX: Just copied trans_alu...think again..
+ RtlDst = hipe_rtl:fp_dst(I),
+ RtlSrc1 = hipe_rtl:fp_src1(I),
+ RtlSrc2 = hipe_rtl:fp_src2(I),
+ %% Destination cannot be a precoloured register
+ FloatTy = hipe_llvm:mk_double(),
+ FloatTyPtr = hipe_llvm:mk_pointer(FloatTy),
+ TmpDst = mk_temp(),
+ {Src1, I1} = trans_float_src(RtlSrc1),
+ {Src2, I2} = trans_float_src(RtlSrc2),
+ Op = trans_fp_op(hipe_rtl:fp_op(I)),
+ I3 = hipe_llvm:mk_operation(TmpDst, Op, FloatTy, Src1, Src2, []),
+ I4 = store_float_stack(TmpDst, RtlDst),
+ %% Synchronization for floating point exceptions
+ I5 = hipe_llvm:mk_store(FloatTy, TmpDst, FloatTyPtr, "%exception_sync", [],
+ [], true),
+ T1 = mk_temp(),
+ I6 = hipe_llvm:mk_load(T1, FloatTyPtr, "%exception_sync", [], [], true),
+ {[I6, I5, I4, I3, I2, I1], Relocs}.
+
+%%
+%% fp_unop
+%%
+trans_fp_unop(I, Relocs) ->
+ RtlDst = hipe_rtl:fp_unop_dst(I),
+ RtlSrc = hipe_rtl:fp_unop_src(I),
+ %% Destination cannot be a precoloured register
+ TmpDst = mk_temp(),
+ {Src, I1} = trans_float_src(RtlSrc),
+ Op = trans_fp_op(hipe_rtl:fp_unop_op(I)),
+ FloatTy = hipe_llvm:mk_double(),
+ I2 = hipe_llvm:mk_operation(TmpDst, Op, FloatTy, "0.0", Src, []),
+ I3 = store_float_stack(TmpDst, RtlDst),
+ {[I3, I2, I1], Relocs}.
+%% TODO: Fix fp_unop in a way like the following. You must change trans_dest,
+%% in order to call float_to_list in a case of float constant. Maybe the type
+%% check is expensive...
+%% Dst = hipe_rtl:fp_unop_dst(I),
+%% Src = hipe_rtl:fp_unop_src(I),
+%% Op = hipe_rtl:fp_unop_op(I),
+%% Zero = hipe_rtl:mk_imm(0.0),
+%% I1 = hipe_rtl:mk_fp(Dst, Zero, Op, Src),
+%% trans_fp(I, Relocs1).
+
+%%
+%% fstore
+%%
+trans_fstore(I, Relocs) ->
+ Base = hipe_rtl:fstore_base(I),
+ case isPrecoloured(Base) of
+ true ->
+ trans_fstore_reg(I, Relocs);
+ false ->
+ exit({?MODULE, trans_fstore ,{"Not implemented yet", false}})
+ end.
+
+trans_fstore_reg(I, Relocs) ->
+ {Base, I0} = trans_reg(hipe_rtl:fstore_base(I), dst),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ FloatTy = hipe_llvm:mk_double(),
+ FloatTyPtr = hipe_llvm:mk_pointer(FloatTy),
+ T1 = mk_temp(),
+ I1 = hipe_llvm:mk_load(T1, WordTyPtr, Base, [], [], false),
+ {Offset, I2} = trans_src(hipe_rtl:fstore_offset(I)),
+ T2 = mk_temp(),
+ I3 = hipe_llvm:mk_operation(T2, add, WordTy, T1, Offset, []),
+ T3 = mk_temp(),
+ I4 = hipe_llvm:mk_conversion(T3, inttoptr, WordTy, T2, FloatTyPtr),
+ {Value, I5} = trans_src(hipe_rtl:fstore_src(I)),
+ I6 = hipe_llvm:mk_store(FloatTy, Value, FloatTyPtr, T3, [], [], false),
+ {[I6, I5, I4, I3, I2, I1, I0], Relocs}.
+
+%%
+%% goto
+%%
+trans_goto(I, Relocs) ->
+ I1 = hipe_llvm:mk_br(mk_jump_label(hipe_rtl:goto_label(I))),
+ {I1, Relocs}.
+
+%%
+%% label
+%%
+trans_label(I, Relocs) ->
+ Label = mk_label(hipe_rtl:label_name(I)),
+ I1 = hipe_llvm:mk_label(Label),
+ {I1, Relocs}.
+
+%%
+%% load
+%%
+trans_load(I, Relocs) ->
+ RtlDst = hipe_rtl:load_dst(I),
+ TmpDst = mk_temp(),
+ %% XXX: Why translate them independently? ------------------------
+ {Src, I1} = trans_src(hipe_rtl:load_src(I)),
+ {Offset, I2} = trans_src(hipe_rtl:load_offset(I)),
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ I3 = hipe_llvm:mk_operation(T1, add, WordTy, Src, Offset, []),
+ %%----------------------------------------------------------------
+ I4 = case hipe_rtl:load_size(I) of
+ word ->
+ T2 = mk_temp(),
+ II1 = hipe_llvm:mk_conversion(T2, inttoptr, WordTy, T1, WordTyPtr),
+ II2 = hipe_llvm:mk_load(TmpDst, WordTyPtr, T2, [], [], false),
+ [II2, II1];
+ Size ->
+ LoadType = llvm_type_from_size(Size),
+ LoadTypeP = hipe_llvm:mk_pointer(LoadType),
+ T2 = mk_temp(),
+ II1 = hipe_llvm:mk_conversion(T2, inttoptr, WordTy, T1, LoadTypeP),
+ T3 = mk_temp(),
+ LoadTypePointer = hipe_llvm:mk_pointer(LoadType),
+ II2 = hipe_llvm:mk_load(T3, LoadTypePointer, T2, [], [], false),
+ Conversion =
+ case hipe_rtl:load_sign(I) of
+ signed -> sext;
+ unsigned -> zext
+ end,
+ II3 =
+ hipe_llvm:mk_conversion(TmpDst, Conversion, LoadType, T3, WordTy),
+ [II3, II2, II1]
+ end,
+ I5 = store_stack_dst(TmpDst, RtlDst),
+ {[I5, I4, I3, I2, I1], Relocs}.
+
+%%
+%% load_address
+%%
+trans_load_address(I, Relocs) ->
+ RtlDst = hipe_rtl:load_address_dst(I),
+ RtlAddr = hipe_rtl:load_address_addr(I),
+ {Addr, NewRelocs} =
+ case hipe_rtl:load_address_type(I) of
+ constant ->
+ {"%DL" ++ integer_to_list(RtlAddr) ++ "_var", Relocs};
+ closure ->
+ {{_, ClosureName, _}, _, _} = RtlAddr,
+ FixedClosureName = fix_closure_name(ClosureName),
+ Relocs1 = relocs_store(FixedClosureName, {closure, RtlAddr}, Relocs),
+ {"%" ++ FixedClosureName ++ "_var", Relocs1};
+ type ->
+ exit({?MODULE, trans_load_address,
+ {"Type not implemented in load_address", RtlAddr}})
+ end,
+ I1 = store_stack_dst(Addr, RtlDst),
+ {[I1], NewRelocs}.
+
+%%
+%% load_atom
+%%
+trans_load_atom(I, Relocs) ->
+ RtlDst = hipe_rtl:load_atom_dst(I),
+ RtlAtom = hipe_rtl:load_atom_atom(I),
+ AtomName = "atom_" ++ make_llvm_id(atom_to_list(RtlAtom)),
+ AtomVar = "%" ++ AtomName ++ "_var",
+ NewRelocs = relocs_store(AtomName, {atom, RtlAtom}, Relocs),
+ I1 = store_stack_dst(AtomVar, RtlDst),
+ {[I1], NewRelocs}.
+
+%%
+%% move
+%%
+trans_move(I, Relocs) ->
+ RtlDst = hipe_rtl:move_dst(I),
+ RtlSrc = hipe_rtl:move_src(I),
+ {Src, I1} = trans_src(RtlSrc),
+ I2 = store_stack_dst(Src, RtlDst),
+ {[I2, I1], Relocs}.
+
+%%
+%% return
+%%
+trans_return(I, Relocs) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ {VarRet, I1} =
+ case hipe_rtl:return_varlist(I) of
+ [] ->
+ {[], []};
+ [A] ->
+ {Name, II1} = trans_src(A),
+ {[{WordTy, Name}], II1}
+ end,
+ FixedRegs = fixed_registers(),
+ {LoadedFixedRegs, I2} = load_fixed_regs(FixedRegs),
+ FixedRet = [{WordTy, X} || X <- LoadedFixedRegs],
+ Ret = FixedRet ++ VarRet,
+ {RetTypes, _RetNames} = lists:unzip(Ret),
+ Type = hipe_llvm:mk_struct(RetTypes),
+ {RetStruct, I3} = mk_return_struct(Ret, Type),
+ I4 = hipe_llvm:mk_ret([{Type, RetStruct}]),
+ {[I4, I3, I2, I1], Relocs}.
+
+%% @doc Create a structure to hold the return value and the precoloured
+%% registers.
+mk_return_struct(RetValues, Type) ->
+ mk_return_struct(RetValues, Type, [], "undef", 0).
+
+mk_return_struct([], _, Acc, StructName, _) ->
+ {StructName, Acc};
+mk_return_struct([{ElemType, ElemName}|Rest], Type, Acc, StructName, Index) ->
+ T1 = mk_temp(),
+ I1 = hipe_llvm:mk_insertvalue(T1, Type, StructName, ElemType, ElemName,
+ integer_to_list(Index), []),
+ mk_return_struct(Rest, Type, [I1 | Acc], T1, Index+1).
+
+%%
+%% store
+%%
+trans_store(I, Relocs) ->
+ {Base, I1} = trans_src(hipe_rtl:store_base(I)),
+ {Offset, I2} = trans_src(hipe_rtl:store_offset(I)),
+ {Value, I3} = trans_src(hipe_rtl:store_src(I)),
+ T1 = mk_temp(),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ I4 = hipe_llvm:mk_operation(T1, add, WordTy, Base, Offset, []),
+ I5 =
+ case hipe_rtl:store_size(I) of
+ word ->
+ T2 = mk_temp(),
+ II1 = hipe_llvm:mk_conversion(T2, inttoptr, WordTy, T1, WordTyPtr),
+ II2 = hipe_llvm:mk_store(WordTy, Value, WordTyPtr, T2, [], [],
+ false),
+ [II2, II1];
+ Size ->
+ %% XXX: Is always trunc correct ?
+ LoadType = llvm_type_from_size(Size),
+ LoadTypePointer = hipe_llvm:mk_pointer(LoadType),
+ T2 = mk_temp(),
+ II1 = hipe_llvm:mk_conversion(T2, inttoptr, WordTy, T1, LoadTypePointer),
+ T3 = mk_temp(),
+ II2 = hipe_llvm:mk_conversion(T3, 'trunc', WordTy, Value, LoadType),
+ II3 = hipe_llvm:mk_store(LoadType, T3, LoadTypePointer, T2, [], [], false),
+ [II3, II2, II1]
+ end,
+ {[I5, I4, I3, I2, I1], Relocs}.
+
+%%
+%% switch
+%%
+trans_switch(I, Relocs, Data) ->
+ RtlSrc = hipe_rtl:switch_src(I),
+ {Src, I1} = trans_src(RtlSrc),
+ Labels = hipe_rtl:switch_labels(I),
+ JumpLabels = [mk_jump_label(L) || L <- Labels],
+ SortOrder = hipe_rtl:switch_sort_order(I),
+ NrLabels = length(Labels),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ TableType = hipe_llvm:mk_array(NrLabels, ByteTyPtr),
+ TableTypeP = hipe_llvm:mk_pointer(TableType),
+ TypedJumpLabels = [{hipe_llvm:mk_label_type(), X} || X <- JumpLabels],
+ T1 = mk_temp(),
+ {Src2, []} = trans_dst(RtlSrc),
+ TableName = "table_" ++ tl(Src2),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I2 = hipe_llvm:mk_getelementptr(T1, TableTypeP, "@"++TableName,
+ [{WordTy, "0"}, {WordTy, Src}], false),
+ T2 = mk_temp(),
+ BYTE_TYPE_PP = hipe_llvm:mk_pointer(ByteTyPtr),
+ I3 = hipe_llvm:mk_load(T2, BYTE_TYPE_PP, T1, [], [], false),
+ I4 = hipe_llvm:mk_indirectbr(ByteTyPtr, T2, TypedJumpLabels),
+ LMap = [{label, L} || L <- Labels],
+ %% Update data with the info for the jump table
+ {NewData, JTabLab} =
+ case hipe_rtl:switch_sort_order(I) of
+ [] ->
+ hipe_consttab:insert_block(Data, word, LMap);
+ SortOrder ->
+ hipe_consttab:insert_sorted_block(Data, word, LMap, SortOrder)
+ end,
+ Relocs2 = relocs_store(TableName, {switch, {TableType, Labels, NrLabels,
+ SortOrder}, JTabLab}, Relocs),
+ {[I4, I3, I2, I1], Relocs2, NewData}.
+
+%% @doc Pass on RTL code in order to fix invoke and closure calls.
+fix_code(Code) ->
+ fix_calls(Code).
+
+%% @doc Fix invoke calls and closure calls with more than ?NR_ARG_REGS
+%% arguments.
+fix_calls(Code) ->
+ fix_calls(Code, [], []).
+
+fix_calls([], Acc, FailLabels) ->
+ {lists:reverse(Acc), FailLabels};
+fix_calls([I | Is], Acc, FailLabels) ->
+ case hipe_rtl:is_call(I) of
+ true ->
+ {NewCall, NewFailLabels} =
+ case hipe_rtl:call_fail(I) of
+ [] ->
+ {I, FailLabels};
+ FailLabel ->
+ fix_invoke_call(I, FailLabel, FailLabels)
+ end,
+ fix_calls(Is, [NewCall|Acc], NewFailLabels);
+ false ->
+ fix_calls(Is, [I|Acc], FailLabels)
+ end.
+
+%% @doc When a call has a fail continuation label it must be extended with a
+%% normal continuation label to go with the LLVM's invoke instruction.
+%% FailLabels is the list of labels of all fail blocks, which are needed to
+%% be declared as landing pads. Furtermore, we must add to fail labels a
+%% call to hipe_bifs:llvm_fix_pinned_regs/0 in order to avoid reloading old
+%% values of pinned registers. This may happen because the result of an
+%% invoke instruction is not available at fail-labels, and, thus, we cannot
+%% get the correct values of pinned registers. Finally, the stack needs to
+%% be re-adjusted when there are stack arguments.
+fix_invoke_call(I, FailLabel, FailLabels) ->
+ NewLabel = hipe_gensym:new_label(llvm),
+ NewCall1 = hipe_rtl:call_normal_update(I, NewLabel),
+ SpAdj = find_sp_adj(hipe_rtl:call_arglist(I)),
+ case lists:keyfind(FailLabel, 1, FailLabels) of
+ %% Same fail label with same Stack Pointer adjustment
+ {FailLabel, NewFailLabel, SpAdj} ->
+ NewCall2 = hipe_rtl:call_fail_update(NewCall1, NewFailLabel),
+ {NewCall2, FailLabels};
+ %% Same fail label but with different Stack Pointer adjustment
+ {_, _, _} ->
+ NewFailLabel = hipe_gensym:new_label(llvm),
+ NewCall2 = hipe_rtl:call_fail_update(NewCall1, NewFailLabel),
+ {NewCall2, [{FailLabel, NewFailLabel, SpAdj} | FailLabels]};
+ %% New Fail label
+ false ->
+ NewFailLabel = hipe_gensym:new_label(llvm),
+ NewCall2 = hipe_rtl:call_fail_update(NewCall1, NewFailLabel),
+ {NewCall2, [{FailLabel, NewFailLabel, SpAdj} | FailLabels]}
+ end.
+
+find_sp_adj(ArgList) ->
+ NrArgs = length(ArgList),
+ case NrArgs > ?NR_ARG_REGS of
+ true ->
+ (NrArgs - ?NR_ARG_REGS) * hipe_rtl_arch:word_size();
+ false ->
+ 0
+ end.
+
+%% @doc Add landingpad instruction in Fail Blocks.
+add_landingpads(LLVM_Code, FailLabels) ->
+ FailLabels2 = [convert_label(T) || T <- FailLabels],
+ add_landingpads(LLVM_Code, FailLabels2, []).
+
+add_landingpads([], _, Acc) ->
+ lists:reverse(Acc);
+add_landingpads([I | Is], FailLabels, Acc) ->
+ case hipe_llvm:is_label(I) of
+ true ->
+ Label = hipe_llvm:label_label(I),
+ Ins = create_fail_blocks(Label, FailLabels),
+ add_landingpads(Is, FailLabels, [I | Ins] ++ Acc);
+ false ->
+ add_landingpads(Is, FailLabels, [I | Acc])
+ end.
+
+convert_label({X,Y,Z}) ->
+ {"L" ++ integer_to_list(X), "FL" ++ integer_to_list(Y), Z}.
+
+%% @doc Create a fail block wich.
+create_fail_blocks(_, []) -> [];
+create_fail_blocks(Label, FailLabels) ->
+ create_fail_blocks(Label, FailLabels, []).
+
+create_fail_blocks(Label, FailLabels, Acc) ->
+ case lists:keytake(Label, 1, FailLabels) of
+ false ->
+ Acc;
+ {value, {Label, FailLabel, SpAdj}, RestFailLabels} ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ I1 = hipe_llvm:mk_label(FailLabel),
+ LP = hipe_llvm:mk_landingpad(),
+ I2 =
+ case SpAdj > 0 of
+ true ->
+ StackPointer = ?ARCH_REGISTERS:reg_name(?ARCH_REGISTERS:sp()),
+ hipe_llvm:mk_adj_stack(integer_to_list(SpAdj), StackPointer,
+ WordTy);
+ false -> []
+ end,
+ T1 = mk_temp(),
+ FixedRegs = fixed_registers(),
+ FunRetTy =
+ hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ I3 = hipe_llvm:mk_call(T1, false, "cc 11", [], FunRetTy,
+ "@hipe_bifs.llvm_fix_pinned_regs.0", [], []),
+ I4 = store_fixed_regs(FixedRegs, T1),
+ I5 = hipe_llvm:mk_br("%" ++ Label),
+ Ins = lists:flatten([I5, I4, I3, I2, LP,I1]),
+ create_fail_blocks(Label, RestFailLabels, Ins ++ Acc)
+ end.
+
+%%------------------------------------------------------------------------------
+%% Miscellaneous Functions
+%%------------------------------------------------------------------------------
+
+%% @doc Convert RTL argument list to LLVM argument list.
+trans_args(ArgList) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ MakeArg =
+ fun(A) ->
+ {Name, I1} = trans_src(A),
+ {{WordTy, Name}, I1}
+ end,
+ [MakeArg(A) || A <- ArgList].
+
+%% @doc Convert a list of Precoloured registers to LLVM argument list.
+fix_reg_args(ArgList) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ [{WordTy, A} || A <- ArgList].
+
+%% @doc Load Precoloured registers.
+load_fixed_regs(RegList) ->
+ Names = [mk_temp_reg(R) || R <- RegList],
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ Fun1 =
+ fun (X, Y) ->
+ hipe_llvm:mk_load(X, WordTyPtr, "%" ++ Y ++ "_reg_var", [], [], false)
+ end,
+ Ins = lists:zipwith(Fun1, Names, RegList),
+ {Names, Ins}.
+
+%% @doc Store Precoloured registers.
+store_fixed_regs(RegList, Name) ->
+ Names = [mk_temp_reg(R) || R <- RegList],
+ Indexes = lists:seq(0, erlang:length(RegList) - 1),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ Fun1 =
+ fun(X,Y) ->
+ hipe_llvm:mk_extractvalue(X, FunRetTy, Name, integer_to_list(Y), [])
+ end,
+ I1 = lists:zipwith(Fun1, Names, Indexes),
+ Fun2 =
+ fun (X, Y) ->
+ hipe_llvm:mk_store(WordTy, X, WordTyPtr, "%" ++ Y ++ "_reg_var", [], [],
+ false)
+ end,
+ I2 = lists:zipwith(Fun2, Names, RegList),
+ [I2, I1].
+
+%%------------------------------------------------------------------------------
+%% Translation of Names
+%%------------------------------------------------------------------------------
+
+%% @doc Fix F in MFA tuple to acceptable LLVM identifier (case of closure).
+-spec fix_mfa_name(mfa()) -> mfa().
+fix_mfa_name({Mod_Name, Closure_Name, Arity}) ->
+ Fun_Name = list_to_atom(fix_closure_name(Closure_Name)),
+ {Mod_Name, Fun_Name, Arity}.
+
+%% @doc Make an acceptable LLVM identifier for a closure name.
+fix_closure_name(ClosureName) ->
+ make_llvm_id(atom_to_list(ClosureName)).
+
+%% @doc Create an acceptable LLVM identifier.
+make_llvm_id(Name) ->
+ case Name of
+ "" -> "Empty";
+ Other -> lists:flatten([llvm_id(C) || C <- Other])
+ end.
+
+llvm_id(C) when C=:=46; C>47 andalso C<58; C>64 andalso C<91; C=:=95;
+ C>96 andalso C<123 ->
+ C;
+llvm_id(C) ->
+ io_lib:format("_~2.16.0B_",[C]).
+
+%% @doc Create an acceptable LLVM identifier for an MFA.
+trans_mfa_name({M,F,A}) ->
+ N = atom_to_list(M) ++ "." ++ atom_to_list(F) ++ "." ++ integer_to_list(A),
+ make_llvm_id(N).
+
+%%------------------------------------------------------------------------------
+%% Creation of Labels and Temporaries
+%%------------------------------------------------------------------------------
+mk_label(N) ->
+ "L" ++ integer_to_list(N).
+
+mk_jump_label(N) ->
+ "%L" ++ integer_to_list(N).
+
+mk_temp() ->
+ "%t" ++ integer_to_list(hipe_gensym:new_var(llvm)).
+
+mk_temp_reg(Name) ->
+ "%" ++ Name ++ integer_to_list(hipe_gensym:new_var(llvm)).
+
+%%----------------------------------------------------------------------------
+%% Translation of Operands
+%%----------------------------------------------------------------------------
+
+store_stack_dst(TempDst, Dst) ->
+ {Dst2, II1} = trans_dst(Dst),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ II2 = hipe_llvm:mk_store(WordTy, TempDst, WordTyPtr, Dst2, [], [], false),
+ [II2, II1].
+
+store_float_stack(TempDst, Dst) ->
+ {Dst2, II1} = trans_dst(Dst),
+ FloatTy = hipe_llvm:mk_double(),
+ FloatTyPtr = hipe_llvm:mk_pointer(FloatTy),
+ II2 = hipe_llvm:mk_store(FloatTy, TempDst, FloatTyPtr, Dst2, [], [], false),
+ [II2, II1].
+
+trans_float_src(Src) ->
+ case hipe_rtl:is_const_label(Src) of
+ true ->
+ Name = "@DL" ++ integer_to_list(hipe_rtl:const_label_label(Src)),
+ T1 = mk_temp(),
+ %% XXX: Hardcoded offset
+ ByteTy = hipe_llvm:mk_int(8),
+ ByteTyPtr = hipe_llvm:mk_pointer(ByteTy),
+ I1 = hipe_llvm:mk_getelementptr(T1, ByteTyPtr, Name,
+ [{ByteTy, integer_to_list(?FLOAT_OFFSET)}], true),
+ T2 = mk_temp(),
+ FloatTy = hipe_llvm:mk_double(),
+ FloatTyPtr = hipe_llvm:mk_pointer(FloatTy),
+ I2 = hipe_llvm:mk_conversion(T2, bitcast, ByteTyPtr, T1, FloatTyPtr),
+ T3 = mk_temp(),
+ I3 = hipe_llvm:mk_load(T3, FloatTyPtr, T2, [], [], false),
+ {T3, [I3, I2, I1]};
+ false ->
+ trans_src(Src)
+ end.
+
+trans_src(A) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ case hipe_rtl:is_imm(A) of
+ true ->
+ Value = integer_to_list(hipe_rtl:imm_value(A)),
+ {Value, []};
+ false ->
+ case hipe_rtl:is_reg(A) of
+ true ->
+ case isPrecoloured(A) of
+ true -> trans_reg(A, src);
+ false ->
+ {Name, []} = trans_reg(A, src),
+ T1 = mk_temp(),
+ I1 = hipe_llvm:mk_load(T1, WordTyPtr, Name, [], [], false),
+ {T1, [I1]}
+ end;
+ false ->
+ case hipe_rtl:is_var(A) of
+ true ->
+ RootName = "%vr" ++ integer_to_list(hipe_rtl:var_index(A)),
+ T1 = mk_temp(),
+ I1 = hipe_llvm:mk_load(T1, WordTyPtr, RootName, [], [], false),
+ I2 =
+ case hipe_rtl:var_liveness(A) of
+ live ->
+ [];
+ dead ->
+ NilValue = hipe_tagscheme:mk_nil(),
+ hipe_llvm:mk_store(WordTy, integer_to_list(NilValue), WordTyPtr, RootName,
+ [], [], false)
+ end,
+ {T1, [I2, I1]};
+ false ->
+ case hipe_rtl:is_fpreg(A) of
+ true ->
+ {Name, []} = trans_dst(A),
+ T1 = mk_temp(),
+ FloatTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_double()),
+ I1 = hipe_llvm:mk_load(T1, FloatTyPtr, Name, [], [], false),
+ {T1, [I1]};
+ false -> trans_dst(A)
+ end
+ end
+ end
+ end.
+
+trans_dst(A) ->
+ case hipe_rtl:is_reg(A) of
+ true ->
+ trans_reg(A, dst);
+ false ->
+ Name = case hipe_rtl:is_var(A) of
+ true ->
+ "%vr" ++ integer_to_list(hipe_rtl:var_index(A));
+ false ->
+ case hipe_rtl:is_fpreg(A) of
+ true -> "%fr" ++ integer_to_list(hipe_rtl:fpreg_index(A));
+ false ->
+ case hipe_rtl:is_const_label(A) of
+ true ->
+ "%DL" ++ integer_to_list(hipe_rtl:const_label_label(A)) ++ "_var";
+ false ->
+ exit({?MODULE, trans_dst, {"Bad RTL argument",A}})
+ end
+ end
+ end,
+ {Name, []}
+ end.
+
+%% @doc Translate a register. If it is precoloured it must be mapped to the
+%% correct stack slot that holds the precoloured register value.
+trans_reg(Arg, Position) ->
+ Index = hipe_rtl:reg_index(Arg),
+ case isPrecoloured(Arg) of
+ true ->
+ Name = map_precoloured_reg(Index),
+ case Position of
+ src -> fix_reg_src(Name);
+ dst -> fix_reg_dst(Name)
+ end;
+ false ->
+ {hipe_rtl_arch:reg_name(Index), []}
+ end.
+
+map_precoloured_reg(Index) ->
+ case hipe_rtl_arch:reg_name(Index) of
+ "%r15" -> "%hp_reg_var";
+ "%rbp" -> "%p_reg_var";
+ "%esi" -> "%hp_reg_var";
+ "%ebp" -> "%p_reg_var";
+ "%fcalls" ->
+ {"%p_reg_var", ?ARCH_REGISTERS:proc_offset(?ARCH_REGISTERS:fcalls())};
+ "%hplim" ->
+ {"%p_reg_var", ?ARCH_REGISTERS:proc_offset(?ARCH_REGISTERS:heap_limit())};
+ _ ->
+ exit({?MODULE, map_precoloured_reg, {"Register not mapped yet", Index}})
+ end.
+
+%% @doc Load precoloured dst register.
+fix_reg_dst(Register) ->
+ case Register of
+ {Name, Offset} -> %% Case of %fcalls, %hplim
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ pointer_from_reg(Name, WordTy, Offset);
+ Name -> %% Case of %p and %hp
+ {Name, []}
+ end.
+
+%% @doc Load precoloured src register.
+fix_reg_src(Register) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ case Register of
+ {Name, Offset} -> %% Case of %fcalls, %hplim
+ {T1, I1} = pointer_from_reg(Name, WordTy, Offset),
+ T2 = mk_temp(),
+ I2 = hipe_llvm:mk_load(T2, WordTyPtr, T1, [], [] , false),
+ {T2, [I2, I1]};
+ Name -> %% Case of %p and %hp
+ T1 = mk_temp(),
+ {T1, hipe_llvm:mk_load(T1, WordTyPtr, Name, [], [], false)}
+ end.
+
+%% @doc Load %fcalls and %hplim.
+pointer_from_reg(RegName, Type, Offset) ->
+ PointerType = hipe_llvm:mk_pointer(Type),
+ T1 = mk_temp(),
+ I1 = hipe_llvm:mk_load(T1, PointerType, RegName, [], [] ,false),
+ T2 = mk_temp(),
+ I2 = hipe_llvm:mk_conversion(T2, inttoptr, Type, T1, PointerType),
+ T3 = mk_temp(),
+ %% XXX: Offsets should be a power of 2.
+ I3 = hipe_llvm:mk_getelementptr(T3, PointerType, T2,
+ [{Type, integer_to_list(Offset div hipe_rtl_arch:word_size())}], true),
+ {T3, [I3, I2, I1]}.
+
+isPrecoloured(X) ->
+ hipe_rtl_arch:is_precoloured(X).
+
+%%------------------------------------------------------------------------------
+%% Translation of operators
+%%------------------------------------------------------------------------------
+
+trans_op(Op) ->
+ case Op of
+ add -> add;
+ sub -> sub;
+ 'or' -> 'or';
+ 'and' -> 'and';
+ 'xor' -> 'xor';
+ sll -> shl;
+ srl -> lshr;
+ sra -> ashr;
+ mul -> mul;
+ 'fdiv' -> fdiv;
+ 'sdiv' -> sdiv;
+ 'srem' -> srem;
+ Other -> exit({?MODULE, trans_op, {"Unknown RTL operator", Other}})
+ end.
+
+trans_rel_op(Op) ->
+ case Op of
+ eq -> eq;
+ ne -> ne;
+ gtu -> ugt;
+ geu -> uge;
+ ltu -> ult;
+ leu -> ule;
+ gt -> sgt;
+ ge -> sge;
+ lt -> slt;
+ le -> sle
+ end.
+
+trans_prim_op(Op) ->
+ case Op of
+ '+' -> "bif_add";
+ '-' -> "bif_sub";
+ '*' -> "bif_mul";
+ 'div' -> "bif_div";
+ '/' -> "bif_div";
+ Other -> atom_to_list(Other)
+ end.
+
+trans_fp_op(Op) ->
+ case Op of
+ fadd -> fadd;
+ fsub -> fsub;
+ fdiv -> fdiv;
+ fmul -> fmul;
+ fchs -> fsub;
+ Other -> exit({?MODULE, trans_fp_op, {"Unknown RTL float operator",Other}})
+ end.
+
+%% Misc.
+insn_dst(I) ->
+ case I of
+ #alu{} ->
+ [hipe_rtl:alu_dst(I)];
+ #alub{} ->
+ [hipe_rtl:alub_dst(I)];
+ #call{} ->
+ case hipe_rtl:call_dstlist(I) of
+ [] -> [];
+ [Dst] -> [Dst]
+ end;
+ #load{} ->
+ [hipe_rtl:load_dst(I)];
+ #load_address{} ->
+ [hipe_rtl:load_address_dst(I)];
+ #load_atom{} ->
+ [hipe_rtl:load_atom_dst(I)];
+ #move{} ->
+ [hipe_rtl:move_dst(I)];
+ #phi{} ->
+ [hipe_rtl:phi_dst(I)];
+ #fconv{} ->
+ [hipe_rtl:fconv_dst(I)];
+ #fload{} ->
+ [hipe_rtl:fload_dst(I)];
+ #fmove{} ->
+ [hipe_rtl:fmove_dst(I)];
+ #fp{} ->
+ [hipe_rtl:fp_dst(I)];
+ #fp_unop{} ->
+ [hipe_rtl:fp_unop_dst(I)];
+ _ ->
+ []
+ end.
+
+llvm_type_from_size(Size) ->
+ case Size of
+ byte -> hipe_llvm:mk_int(8);
+ int16 -> hipe_llvm:mk_int(16);
+ int32 -> hipe_llvm:mk_int(32);
+ word -> hipe_llvm:mk_int(64)
+ end.
+
+%% @doc Create definition for the compiled function. The parameters that are
+%% passed to the stack must be reversed to match with the CC. Also
+%% precoloured registers that are passed as arguments must be stored to
+%% the corresonding stack slots.
+create_function_definition(Fun, Params, Code, LocalVars) ->
+ FunctionName = trans_mfa_name(Fun),
+ FixedRegs = fixed_registers(),
+ %% Reverse parameters to match with the Erlang calling convention
+ ReversedParams =
+ case erlang:length(Params) > ?NR_ARG_REGS of
+ false ->
+ Params;
+ true ->
+ {ParamsInRegs, ParamsInStack} = lists:split(?NR_ARG_REGS, Params),
+ ParamsInRegs ++ lists:reverse(ParamsInStack)
+ end,
+ Args = header_regs(FixedRegs) ++ header_params(ReversedParams),
+ EntryLabel = hipe_llvm:mk_label("Entry"),
+ FloatTy = hipe_llvm:mk_double(),
+ ExceptionSync = hipe_llvm:mk_alloca("%exception_sync", FloatTy, [], []),
+ I2 = load_regs(FixedRegs),
+ I3 = hipe_llvm:mk_br(mk_jump_label(get(first_label))),
+ StoredParams = store_params(Params),
+ EntryBlock =
+ lists:flatten([EntryLabel, ExceptionSync, I2, LocalVars, StoredParams, I3]),
+ Final_Code = EntryBlock ++ Code,
+ FunctionOptions = [nounwind, noredzone, list_to_atom("gc \"erlang\"")],
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ hipe_llvm:mk_fun_def([], [], "cc 11", [], FunRetTy, FunctionName, Args,
+ FunctionOptions, [], Final_Code).
+
+header_params(Params) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ [{WordTy, "%v" ++ integer_to_list(hipe_rtl:var_index(P))} || P <- Params].
+
+store_params(Params) ->
+ Fun1 =
+ fun(X) ->
+ Index = hipe_rtl:var_index(X),
+ {Name, _} = trans_dst(X),
+ ParamName = "%v" ++ integer_to_list(Index),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ hipe_llvm:mk_store(WordTy, ParamName, WordTyPtr, Name, [], [], false)
+ end,
+ lists:map(Fun1, Params).
+
+fixed_registers() ->
+ case get(hipe_target_arch) of
+ x86 ->
+ ["hp", "p"];
+ amd64 ->
+ ["hp", "p"];
+ Other ->
+ exit({?MODULE, map_registers, {"Unknown architecture", Other}})
+ end.
+
+header_regs(Registers) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ [{WordTy, "%" ++ X ++ "_in"} || X <- Registers].
+
+load_regs(Registers) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ Fun1 =
+ fun(X) ->
+ I1 = hipe_llvm:mk_alloca("%" ++ X ++ "_reg_var", WordTy, [], []),
+ I2 = hipe_llvm:mk_store(WordTy, "%" ++ X ++ "_in", WordTyPtr,
+ "%" ++ X ++ "_reg_var", [], [], false),
+ [I1, I2]
+ end,
+ lists:map(Fun1, Registers).
+
+%%------------------------------------------------------------------------------
+%% Relocation-specific Stuff
+%%------------------------------------------------------------------------------
+
+relocs_store(Key, Value, Relocs) ->
+ dict:store(Key, Value, Relocs).
+
+relocs_to_list(Relocs) ->
+ dict:to_list(Relocs).
+
+%% @doc This function is responsible for the actions needed to handle
+%% relocations:
+%% 1) Updates relocations with constants and switch jump tables.
+%% 2) Creates LLVM code to declare relocations as external
+%% functions/constants.
+%% 3) Creates LLVM code in order to create local variables for the external
+%% constants/labels.
+handle_relocations(Relocs, Data, Fun) ->
+ RelocsList = relocs_to_list(Relocs),
+ %% Seperate Relocations according to their type
+ {CallList, AtomList, ClosureList, ClosureLabels, SwitchList} =
+ seperate_relocs(RelocsList),
+ %% Create code to declare atoms
+ AtomDecl = [declare_atom(A) || A <- AtomList],
+ %% Create code to create local name for atoms
+ AtomLoad = [load_atom(A) || A <- AtomList],
+ %% Create code to declare closures
+ ClosureDecl = [declare_closure(C) || C <- ClosureList],
+ %% Create code to create local name for closures
+ ClosureLoad = [load_closure(C) || C <- ClosureList],
+ %% Find function calls
+ IsExternalCall = fun (X) -> is_external_call(X, Fun) end,
+ ExternalCallList = lists:filter(IsExternalCall, CallList),
+ %% Create code to declare external function
+ FunDecl = fixed_fun_decl() ++ [call_to_decl(C) || C <- ExternalCallList],
+ %% Extract constant labels from Constant Map (remove duplicates)
+ ConstLabels = hipe_consttab:labels(Data),
+ %% Create code to declare constants
+ ConstDecl = [declare_constant(C) || C <- ConstLabels],
+ %% Create code to create local name for constants
+ ConstLoad = [load_constant(C) || C <- ConstLabels],
+ %% Create code to create jump tables
+ SwitchDecl = declare_switches(SwitchList, Fun),
+ %% Create code to create a table with the labels of all closure calls
+ {ClosureLabelDecl, Relocs1} =
+ declare_closure_labels(ClosureLabels, Relocs, Fun),
+ %% Enter constants to relocations
+ Relocs2 = lists:foldl(fun const_to_dict/2, Relocs1, ConstLabels),
+ %% Temporary Store inc_stack and llvm_fix_pinned_regs to Dictionary
+ %% TODO: Remove this
+ Relocs3 = dict:store("inc_stack_0", {call, {bif, inc_stack_0, 0}}, Relocs2),
+ Relocs4 = dict:store("hipe_bifs.llvm_fix_pinned_regs.0",
+ {call, {hipe_bifs, llvm_fix_pinned_regs, 0}}, Relocs3),
+ BranchMetaData = [
+ hipe_llvm:mk_branch_meta(?BRANCH_META_TAKEN, "99", "1")
+ , hipe_llvm:mk_branch_meta(?BRANCH_META_NOT_TAKEN, "1", "99")
+ ],
+ ExternalDeclarations = AtomDecl ++ ClosureDecl ++ ConstDecl ++ FunDecl ++
+ ClosureLabelDecl ++ SwitchDecl ++ BranchMetaData,
+ LocalVariables = AtomLoad ++ ClosureLoad ++ ConstLoad,
+ {Relocs4, ExternalDeclarations, LocalVariables}.
+
+%% @doc Seperate relocations according to their type.
+seperate_relocs(Relocs) ->
+ seperate_relocs(Relocs, [], [], [], [], []).
+
+seperate_relocs([], CallAcc, AtomAcc, ClosureAcc, LabelAcc, JmpTableAcc) ->
+ {CallAcc, AtomAcc, ClosureAcc, LabelAcc, JmpTableAcc};
+seperate_relocs([R|Rs], CallAcc, AtomAcc, ClosureAcc, LabelAcc, JmpTableAcc) ->
+ case R of
+ {_, {call, _}} ->
+ seperate_relocs(Rs, [R | CallAcc], AtomAcc, ClosureAcc, LabelAcc,
+ JmpTableAcc);
+ {_, {atom, _}} ->
+ seperate_relocs(Rs, CallAcc, [R | AtomAcc], ClosureAcc, LabelAcc,
+ JmpTableAcc);
+ {_, {closure, _}} ->
+ seperate_relocs(Rs, CallAcc, AtomAcc, [R | ClosureAcc], LabelAcc,
+ JmpTableAcc);
+ {_, {switch, _, _}} ->
+ seperate_relocs(Rs, CallAcc, AtomAcc, ClosureAcc, LabelAcc,
+ [R | JmpTableAcc]);
+ {_, {closure_label, _, _}} ->
+ seperate_relocs(Rs, CallAcc, AtomAcc, ClosureAcc, [R | LabelAcc],
+ JmpTableAcc)
+ end.
+
+%% @doc External declaration of an atom.
+declare_atom({AtomName, _}) ->
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ hipe_llvm:mk_const_decl("@" ++ AtomName, "external constant", WordTy, "").
+
+%% @doc Creation of local variable for an atom.
+load_atom({AtomName, _}) ->
+ Dst = "%" ++ AtomName ++ "_var",
+ Name = "@" ++ AtomName,
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ WordTyPtr = hipe_llvm:mk_pointer(WordTy),
+ hipe_llvm:mk_conversion(Dst, ptrtoint, WordTyPtr, Name, WordTy).
+
+%% @doc External declaration of a closure.
+declare_closure({ClosureName, _})->
+ ByteTy = hipe_llvm:mk_int(8),
+ hipe_llvm:mk_const_decl("@" ++ ClosureName, "external constant", ByteTy, "").
+
+%% @doc Creation of local variable for a closure.
+load_closure({ClosureName, _})->
+ Dst = "%" ++ ClosureName ++ "_var",
+ Name = "@" ++ ClosureName,
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ hipe_llvm:mk_conversion(Dst, ptrtoint, ByteTyPtr, Name, WordTy).
+
+%% @doc Declaration of a local variable for a switch jump table.
+declare_switches(JumpTableList, Fun) ->
+ FunName = trans_mfa_name(Fun),
+ [declare_switch_table(X, FunName) || X <- JumpTableList].
+
+declare_switch_table({Name, {switch, {TableType, Labels, _, _}, _}}, FunName) ->
+ LabelList = [mk_jump_label(L) || L <- Labels],
+ Fun1 = fun(X) -> "i8* blockaddress(@" ++ FunName ++ ", " ++ X ++ ")" end,
+ List2 = lists:map(Fun1, LabelList),
+ List3 = string:join(List2, ",\n"),
+ List4 = "[\n" ++ List3 ++ "\n]\n",
+ hipe_llvm:mk_const_decl("@" ++ Name, "constant", TableType, List4).
+
+%% @doc Declaration of a variable for a table with the labels of all closure
+%% calls in the code.
+declare_closure_labels([], Relocs, _Fun) ->
+ {[], Relocs};
+declare_closure_labels(ClosureLabels, Relocs, Fun) ->
+ FunName = trans_mfa_name(Fun),
+ {LabelList, ArityList} =
+ lists:unzip([{mk_jump_label(Label), A} ||
+ {_, {closure_label, Label, A}} <- ClosureLabels]),
+ Relocs1 = relocs_store("table_closures", {table_closures, ArityList}, Relocs),
+ List2 =
+ ["i8* blockaddress(@" ++ FunName ++ ", " ++ L ++ ")" || L <- LabelList],
+ List3 = string:join(List2, ",\n"),
+ List4 = "[\n" ++ List3 ++ "\n]\n",
+ NrLabels = length(LabelList),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ TableType = hipe_llvm:mk_array(NrLabels, ByteTyPtr),
+ ConstDecl =
+ hipe_llvm:mk_const_decl("@table_closures", "constant", TableType, List4),
+ {[ConstDecl], Relocs1}.
+
+%% @doc A call is treated as non external only in a case of a recursive
+%% function.
+is_external_call({_, {call, Fun}}, Fun) -> false;
+is_external_call(_, _) -> true.
+
+%% @doc External declaration of a function.
+call_to_decl({Name, {call, MFA}}) ->
+ {M, _F, A} = MFA,
+ CConv = "cc 11",
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ {Type, Args} =
+ case M of
+ llvm ->
+ {hipe_llvm:mk_struct([WordTy, hipe_llvm:mk_int(1)]), [1, 2]};
+ %% +precoloured regs
+ _ ->
+ {FunRetTy, lists:seq(1, A + ?NR_PINNED_REGS)}
+ end,
+ ArgsTypes = lists:duplicate(length(Args), WordTy),
+ hipe_llvm:mk_fun_decl([], [], CConv, [], Type, "@" ++ Name, ArgsTypes, []).
+
+%% @doc These functions are always declared, even if not used.
+fixed_fun_decl() ->
+ ByteTy = hipe_llvm:mk_int(8),
+ ByteTyPtr = hipe_llvm:mk_pointer(ByteTy),
+ LandPad = hipe_llvm:mk_fun_decl([], [], [], [], hipe_llvm:mk_int(32),
+ "@__gcc_personality_v0", [hipe_llvm:mk_int(32), hipe_llvm:mk_int(64),
+ ByteTyPtr, ByteTyPtr], []),
+ GCROOTDecl = hipe_llvm:mk_fun_decl([], [], [], [], hipe_llvm:mk_void(),
+ "@llvm.gcroot", [hipe_llvm:mk_pointer(ByteTyPtr), ByteTyPtr], []),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)),
+ FixPinnedRegs = hipe_llvm:mk_fun_decl([], [], [], [], FunRetTy,
+ "@hipe_bifs.llvm_fix_pinned_regs.0", [], []),
+ GcMetadata = hipe_llvm:mk_const_decl("@gc_metadata", "external constant",
+ ByteTy, ""),
+ [LandPad, GCROOTDecl, FixPinnedRegs, GcMetadata].
+
+%% @doc Declare an External Consant. We declare all constants as i8 in order to
+%% be able to calcucate pointers of the form DL+6, with the getelementptr
+%% instruction. Otherwise we have to convert constants form pointers to
+%% values, add the offset and convert them again to pointers.
+declare_constant(Label) ->
+ Name = "@DL" ++ integer_to_list(Label),
+ ByteTy = hipe_llvm:mk_int(8),
+ hipe_llvm:mk_const_decl(Name, "external constant", ByteTy, "").
+
+%% @doc Load a constant is achieved by converting a pointer to an integer of
+%% the correct width.
+load_constant(Label) ->
+ Dst = "%DL" ++ integer_to_list(Label) ++ "_var",
+ Name = "@DL" ++ integer_to_list(Label),
+ WordTy = hipe_llvm:mk_int(?WORD_WIDTH),
+ ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)),
+ hipe_llvm:mk_conversion(Dst, ptrtoint, ByteTyPtr, Name, WordTy).
+
+%% @doc Store external constants and calls to dictionary.
+const_to_dict(Elem, Dict) ->
+ Name = "DL" ++ integer_to_list(Elem),
+ dict:store(Name, {'constant', Elem}, Dict).
diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src
index bcdfcb0e03..e81212d4dc 100644
--- a/lib/hipe/main/hipe.app.src
+++ b/lib/hipe/main/hipe.app.src
@@ -30,6 +30,7 @@
cerl_prettypr,
cerl_to_icode,
cerl_typean,
+ elf_format,
erl_bif_types,
erl_types,
hipe,
@@ -108,6 +109,10 @@
hipe_ig,
hipe_ig_moves,
hipe_jit,
+ hipe_llvm,
+ hipe_llvm_liveness,
+ hipe_llvm_main,
+ hipe_llvm_merge,
hipe_ls_regalloc,
hipe_main,
hipe_moves,
@@ -159,6 +164,7 @@
hipe_rtl_symbolic,
hipe_rtl_to_amd64,
hipe_rtl_to_arm,
+ hipe_rtl_to_llvm,
hipe_rtl_to_ppc,
hipe_rtl_to_sparc,
hipe_rtl_to_x86,
@@ -216,4 +222,6 @@
hipe_x86_x87]},
{registered,[]},
{applications, [kernel,stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["syntax_tools-1.6.14","stdlib-2.0","kernel-3.0",
+ "erts-6.0","compiler-5.0"]}]}.
diff --git a/lib/hipe/main/hipe.erl b/lib/hipe/main/hipe.erl
index 434d5c3061..539ce883c0 100644
--- a/lib/hipe/main/hipe.erl
+++ b/lib/hipe/main/hipe.erl
@@ -200,6 +200,7 @@
compile_core/4,
file/1,
file/2,
+ llvm_support_available/0,
load/1,
help/0,
help_hiper/0,
@@ -648,7 +649,18 @@ run_compiler_1(DisasmFun, IcodeFun, Options) ->
%% The full option expansion is not done
%% until the DisasmFun returns.
{Code, CompOpts} = DisasmFun(Options),
- Opts = expand_options(Options ++ CompOpts),
+ Opts0 = expand_options(Options ++ CompOpts),
+ 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])),
@@ -821,7 +833,9 @@ finalize_fun_sequential({MFA, Icode}, Opts, Servers) ->
?debug_msg("Compiled ~w in ~.2f s\n", [MFA,(T2-T1)/1000])),
{MFA, Code};
{rtl, LinearRtl} ->
- {MFA, LinearRtl}
+ {MFA, LinearRtl};
+ {llvm_binary, Binary} ->
+ {MFA, Binary}
catch
error:Error ->
?when_option(verbose, Opts, ?debug_untagged_msg("\n", [])),
@@ -890,21 +904,27 @@ do_load(Mod, Bin, BeamBinOrPath) when is_binary(BeamBinOrPath);
end.
assemble(CompiledCode, Closures, Exports, Options) ->
- case get(hipe_target_arch) of
- ultrasparc ->
- hipe_sparc_assemble:assemble(CompiledCode, Closures, Exports, Options);
- powerpc ->
- hipe_ppc_assemble:assemble(CompiledCode, Closures, Exports, Options);
- ppc64 ->
- hipe_ppc_assemble:assemble(CompiledCode, Closures, Exports, Options);
- arm ->
- hipe_arm_assemble:assemble(CompiledCode, Closures, Exports, Options);
- x86 ->
- hipe_x86_assemble:assemble(CompiledCode, Closures, Exports, Options);
- amd64 ->
- hipe_amd64_assemble:assemble(CompiledCode, Closures, Exports, Options);
- Arch ->
- ?EXIT({executing_on_an_unsupported_architecture, Arch})
+ case proplists:get_bool(to_llvm, Options) of
+ false ->
+ case get(hipe_target_arch) of
+ ultrasparc ->
+ hipe_sparc_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ powerpc ->
+ hipe_ppc_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ ppc64 ->
+ hipe_ppc_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ arm ->
+ hipe_arm_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ x86 ->
+ hipe_x86_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ amd64 ->
+ hipe_amd64_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ Arch ->
+ ?EXIT({executing_on_an_unsupported_architecture, Arch})
+ end;
+ true ->
+ %% Merge already compiled code (per MFA) to a single binary.
+ hipe_llvm_merge:finalize(CompiledCode, Closures, Exports)
end.
%% --------------------------------------------------------------------
@@ -1330,6 +1350,11 @@ opt_keys() ->
timeregalloc,
timers,
to_rtl,
+ to_llvm, % Use the LLVM backend for compilation.
+ llvm_save_temps, % Save the LLVM intermediate files in the current
+ % directory; useful for debugging.
+ llvm_llc, % Specify llc optimization-level: o1, o2, o3, undefined.
+ llvm_opt, % Specify opt optimization-level: o1, o2, o3, undefined.
use_indexing,
use_inline_atom_search,
use_callgraph,
@@ -1468,11 +1493,19 @@ opt_expansions() ->
[{o1, o1_opts()},
{o2, o2_opts()},
{o3, o3_opts()},
+ {to_llvm, llvm_opts(o3)},
+ {{to_llvm, o0}, llvm_opts(o0)},
+ {{to_llvm, o1}, llvm_opts(o1)},
+ {{to_llvm, o2}, llvm_opts(o2)},
+ {{to_llvm, o3}, llvm_opts(o3)},
{x87, [x87, inline_fp]},
{inline_fp, case get(hipe_target_arch) of %% XXX: Temporary until x86
x86 -> [x87, inline_fp]; %% has sse2
_ -> [inline_fp] end}].
+llvm_opts(O) ->
+ [to_llvm, {llvm_opt, O}, {llvm_llc, O}].
+
%% This expands "basic" options, which may be tested early and cannot be
%% in conflict with options found in the source code.
@@ -1516,4 +1549,22 @@ check_options(Opts) ->
ok
end.
+-spec llvm_support_available() -> boolean().
+
+llvm_support_available() ->
+ get_llvm_version() >= 3.4.
+
+get_llvm_version() ->
+ OptStr = os:cmd("opt -version"),
+ SubStr = "LLVM version ", N = length(SubStr),
+ case string:str(OptStr, SubStr) of
+ 0 -> % No opt available
+ 0.0;
+ S ->
+ case string:to_float(string:sub_string(OptStr, S + N)) of
+ {error, _} -> 0.0; %XXX: Assumes no revision numbers in versioning
+ {Float, _} -> Float
+ end
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/hipe/main/hipe_main.erl b/lib/hipe/main/hipe_main.erl
index 99028cc3c1..89b79998be 100644
--- a/lib/hipe/main/hipe_main.erl
+++ b/lib/hipe/main/hipe_main.erl
@@ -49,7 +49,7 @@
%%=====================================================================
-type comp_icode_ret() :: {'native',hipe_architecture(),{'unprofiled',_}}
- | {'rtl',tuple()}.
+ | {'rtl',tuple()} | {'llvm_binary',term()}.
%%=====================================================================
@@ -115,11 +115,18 @@ compile_icode(MFA, LinearIcode0, Options, Servers, DebugState) ->
pp(IcodeCfg7, MFA, icode_liveness, pp_icode_liveness, Options, Servers),
FinalIcode = hipe_icode_cfg:cfg_to_linear(IcodeCfg7),
?opt_stop_timer("Icode"),
- LinearRTL = ?option_time(icode_to_rtl(MFA,FinalIcode,Options, Servers),
- "RTL", Options),
+ {LinearRTL, Roots} = ?option_time(icode_to_rtl(MFA, FinalIcode, Options, Servers),
+ "RTL", Options),
case proplists:get_bool(to_rtl, Options) of
false ->
- rtl_to_native(MFA, LinearRTL, Options, DebugState);
+ case proplists:get_bool(to_llvm, Options) of
+ false ->
+ rtl_to_native(MFA, LinearRTL, Options, DebugState);
+ true ->
+ %% The LLVM backend returns binary code, unlike the rest of the HiPE
+ %% backends which return native assembly.
+ rtl_to_llvm_to_binary(MFA, LinearRTL, Roots, Options, DebugState)
+ end;
true ->
put(hipe_debug, DebugState),
{rtl, LinearRTL}
@@ -385,11 +392,21 @@ icode_to_rtl(MFA, Icode, Options, Servers) ->
%% hipe_rtl_cfg:pp(RtlCfg3),
pp(RtlCfg3, MFA, rtl_liveness, pp_rtl_liveness, Options, Servers),
RtlCfg4 = rtl_lcm(RtlCfg3, Options),
- pp(RtlCfg4, MFA, rtl, pp_rtl, Options, Servers),
- LinearRTL1 = hipe_rtl_cfg:linearize(RtlCfg4),
+ %% LLVM: A liveness analysis on RTL must be performed in order to find the GC
+ %% roots and explicitly mark them (in RTL) when they go out of scope (only
+ %% when the LLVM backend is used).
+ {RtlCfg5, Roots} =
+ case proplists:get_bool(to_llvm, Options) of
+ false ->
+ {RtlCfg4, []};
+ true ->
+ hipe_llvm_liveness:analyze(RtlCfg4)
+ end,
+ pp(RtlCfg5, MFA, rtl, pp_rtl, Options, Servers),
+ LinearRTL1 = hipe_rtl_cfg:linearize(RtlCfg5),
LinearRTL2 = hipe_rtl_cleanup_const:cleanup(LinearRTL1),
%% hipe_rtl:pp(standard_io, LinearRTL2),
- LinearRTL2.
+ {LinearRTL2, Roots}.
translate_to_rtl(Icode, Options) ->
%% GC tests should have been added in the conversion to Icode.
@@ -540,6 +557,17 @@ rtl_to_native(MFA, LinearRTL, Options, DebugState) ->
put(hipe_debug, DebugState),
LinearNativeCode.
+%% Translate Linear RTL to binary code using LLVM.
+rtl_to_llvm_to_binary(MFA, LinearRTL, Roots, Options, DebugState) ->
+ ?opt_start_timer("LLVM native code"),
+ %% BinaryCode is a tuple, as defined in llvm/hipe_llvm_main module, which
+ %% contains the binary code together with info needed by the loader, e.g.
+ %% ConstTab, Refs, LabelMap, etc.
+ BinaryCode = hipe_llvm_main:rtl_to_native(MFA, LinearRTL, Roots, Options),
+ ?opt_stop_timer("LLVM native code"),
+ put(hipe_debug, DebugState),
+ {llvm_binary, BinaryCode}.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Debugging stuff ...
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/hipe/misc/hipe_gensym.erl b/lib/hipe/misc/hipe_gensym.erl
index 84fc8fa7e8..4d2a237188 100644
--- a/lib/hipe/misc/hipe_gensym.erl
+++ b/lib/hipe/misc/hipe_gensym.erl
@@ -44,7 +44,7 @@
%% Types of allowable entities to set global variables for
%%-----------------------------------------------------------------------
--type gvarname() :: 'icode' | 'rtl' | 'arm' | 'ppc' | 'sparc' | 'x86'.
+-type gvarname() :: 'icode' | 'rtl' | 'arm' | 'ppc' | 'sparc' | 'x86' | 'llvm'.
%%-----------------------------------------------------------------------
diff --git a/lib/hipe/misc/hipe_pack_constants.erl b/lib/hipe/misc/hipe_pack_constants.erl
index ca8a9e6bf7..300f9ae43a 100644
--- a/lib/hipe/misc/hipe_pack_constants.erl
+++ b/lib/hipe/misc/hipe_pack_constants.erl
@@ -20,30 +20,48 @@
%%
-module(hipe_pack_constants).
--export([pack_constants/2, slim_refs/1, slim_constmap/1]).
+-export([pack_constants/2, slim_refs/1, slim_constmap/1,
+ find_const/2, mk_data_relocs/2, slim_sorted_exportmap/3]).
-include("hipe_consttab.hrl").
-include("../../kernel/src/hipe_ext_format.hrl").
+-include("../main/hipe.hrl"). % Needed for the EXIT macro in find_const/2.
%%-----------------------------------------------------------------------------
--type raw_data() :: binary() | number() | list() | tuple().
--type tbl_ref() :: {hipe_constlbl(), non_neg_integer()}.
+-type const_num() :: non_neg_integer().
+-type raw_data() :: binary() | number() | list() | tuple().
+
+-type addr() :: non_neg_integer().
+-type ref_p() :: {DataPos :: hipe_constlbl(), CodeOffset :: addr()}.
+-type ref() :: ref_p() | {'sorted', Base :: addr(), [ref_p()]}.
+
+-type mfa_refs() :: {mfa(), [ref()]}.
+
+%% XXX: these types may not belong here: FIX!
+-type fa() :: {atom(), arity()}.
+-type export_map() :: [{addr(), module(), atom(), arity()}].
-record(pcm_entry, {mfa :: mfa(),
label :: hipe_constlbl(),
- const_num :: non_neg_integer(),
- start :: non_neg_integer(),
+ const_num :: const_num(),
+ start :: addr(),
type :: 0 | 1 | 2,
raw_data :: raw_data()}).
+-type pcm_entry() :: #pcm_entry{}.
+
+-type label_map() :: gb_trees:tree({mfa(), hipe_constlbl()}, addr()).
+
+%% Some of the following types may possibly need to be exported
+-type data_relocs() :: [ref()].
+-type packed_const_map() :: [pcm_entry()].
+-type mfa_refs_map() :: [mfa_refs()].
+-type slim_export_map() :: [addr() | module() | atom() | arity() | boolean()].
%%-----------------------------------------------------------------------------
-spec pack_constants([{mfa(),[_],hipe_consttab()}], ct_alignment()) ->
- {ct_alignment(),
- non_neg_integer(),
- [#pcm_entry{}],
- [{mfa(),[tbl_ref() | {'sorted',non_neg_integer(),[tbl_ref()]}]}]}.
+ {ct_alignment(), non_neg_integer(), packed_const_map(), mfa_refs_map()}.
pack_constants(Data, Align) ->
pack_constants(Data, 0, Align, 0, [], []).
@@ -194,13 +212,12 @@ compact_dests([], Dest, AccofDest, Acc) ->
%% to the slimmed and flattened format ConstMap which is put in object
%% files.
%%
--spec slim_constmap([#pcm_entry{}]) -> [raw_data()].
+-spec slim_constmap(packed_const_map()) -> [raw_data()].
slim_constmap(Map) ->
slim_constmap(Map, gb_sets:new(), []).
--spec slim_constmap([#pcm_entry{}], gb_sets:set(), [raw_data()]) -> [raw_data()].
-slim_constmap([#pcm_entry{const_num=ConstNo, start=Offset,
- type=Type, raw_data=Term}|Rest], Inserted, Acc) ->
+slim_constmap([#pcm_entry{const_num = ConstNo, start = Offset,
+ type = Type, raw_data = Term}|Rest], Inserted, Acc) ->
case gb_sets:is_member(ConstNo, Inserted) of
true ->
slim_constmap(Rest, Inserted, Acc);
@@ -209,3 +226,60 @@ slim_constmap([#pcm_entry{const_num=ConstNo, start=Offset,
slim_constmap(Rest, NewInserted, [ConstNo, Offset, Type, Term|Acc])
end;
slim_constmap([], _Inserted, Acc) -> Acc.
+
+%%
+%% Lookup a constant in a ConstMap.
+%%
+-spec find_const({mfa(), hipe_constlbl()}, packed_const_map()) -> const_num().
+
+find_const({MFA, Label}, [E = #pcm_entry{mfa = MFA, label = Label}|_]) ->
+ E#pcm_entry.const_num;
+find_const(N, [_|R]) ->
+ find_const(N, R);
+find_const(C, []) ->
+ ?EXIT({constant_not_found, C}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%
+%% Functions to build and handle Refs, ExportMap and LabelMap.
+%% Note: Moved here because they are used by all backends in
+%% hipe_{arm,sparc,ppc,x86}_assemble.erl
+%% XXX: Is this the right place for them?
+%%
+
+-spec mk_data_relocs(mfa_refs_map(), label_map()) -> data_relocs().
+
+mk_data_relocs(RefsFromConsts, LabelMap) ->
+ lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])).
+
+mk_data_relocs([{MFA, Labels} | Rest], LabelMap, Acc) ->
+ Map = [case Label of
+ {L,Pos} ->
+ Offset = find({MFA,L}, LabelMap),
+ {Pos,Offset};
+ {sorted,Base,OrderedLabels} ->
+ {sorted, Base, [begin
+ Offset = find({MFA,L}, LabelMap),
+ {Order, Offset}
+ end
+ || {L,Order} <- OrderedLabels]}
+ end
+ || Label <- Labels],
+ %% msg("Map: ~w Map\n", [Map]),
+ mk_data_relocs(Rest, LabelMap, [Map,Acc]);
+mk_data_relocs([], _, Acc) -> Acc.
+
+find({MFA,L}, LabelMap) ->
+ gb_trees:get({MFA,L}, LabelMap).
+
+-spec slim_sorted_exportmap(export_map(), [mfa()], [fa()]) -> slim_export_map().
+
+slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) ->
+ IsClosure = lists:member({M,F,A}, Closures),
+ IsExported = is_exported(F, A, Exports),
+ [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)];
+slim_sorted_exportmap([], _, _) -> [].
+
+is_exported(F, A, Exports) ->
+ lists:member({F,A}, Exports).
diff --git a/lib/hipe/ppc/hipe_ppc_assemble.erl b/lib/hipe/ppc/hipe_ppc_assemble.erl
index b2fd50517b..3ad91f4051 100644
--- a/lib/hipe/ppc/hipe_ppc_assemble.erl
+++ b/lib/hipe/ppc/hipe_ppc_assemble.erl
@@ -46,8 +46,8 @@ assemble(CompiledCode, Closures, Exports, Options) ->
print("Total num bytes=~w\n", [CodeSize], Options),
%%
SC = hipe_pack_constants:slim_constmap(ConstMap),
- DataRelocs = mk_data_relocs(RefsFromConsts, LabelMap),
- SSE = slim_sorted_exportmap(ExportMap,Closures,Exports),
+ DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
+ SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap,Closures,Exports),
SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
Bin = term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC},
ConstAlign, ConstSize,
@@ -288,7 +288,7 @@ do_pseudo_li(I, MFA, ConstMap) ->
%%% end,
%%% {load_address, {Tag,untag_mfa_or_prim(MFAorPrim)}};
{Label,constant} ->
- ConstNo = find_const({MFA,Label}, ConstMap),
+ ConstNo = hipe_pack_constants:find_const({MFA,Label}, ConstMap),
{load_address, {constant,ConstNo}};
{Label,closure} ->
{load_address, {closure,Label}};
@@ -574,37 +574,6 @@ mk_y(Pred, BD) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-mk_data_relocs(RefsFromConsts, LabelMap) ->
- lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])).
-
-mk_data_relocs([{MFA,Labels} | Rest], LabelMap, Acc) ->
- Map = [case Label of
- {L,Pos} ->
- Offset = find({MFA,L}, LabelMap),
- {Pos,Offset};
- {sorted,Base,OrderedLabels} ->
- {sorted, Base, [begin
- Offset = find({MFA,L}, LabelMap),
- {Order, Offset}
- end
- || {L,Order} <- OrderedLabels]}
- end
- || Label <- Labels],
- %% msg("Map: ~w Map\n",[Map]),
- mk_data_relocs(Rest, LabelMap, [Map,Acc]);
-mk_data_relocs([],_,Acc) -> Acc.
-
-find({_MFA,_L} = MFAL,LabelMap) ->
- gb_trees:get(MFAL, LabelMap).
-
-slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) ->
- IsClosure = lists:member({M,F,A}, Closures),
- IsExported = is_exported(F, A, Exports),
- [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)];
-slim_sorted_exportmap([],_,_) -> [].
-
-is_exported(F, A, Exports) -> lists:member({F,A}, Exports).
-
%%%
%%% Assembly listing support (pp_asm option).
%%%
@@ -642,14 +611,3 @@ fill_spaces(N) when N > 0 ->
fill_spaces(N-1);
fill_spaces(0) ->
[].
-
-%%%
-%%% Lookup a constant in a ConstMap.
-%%%
-
-find_const({MFA,Label}, [{pcm_entry,MFA,Label,ConstNo,_,_,_}|_]) ->
- ConstNo;
-find_const(N, [_|R]) ->
- find_const(N, R);
-find_const(C, []) ->
- ?EXIT({constant_not_found,C}).
diff --git a/lib/hipe/regalloc/hipe_ls_regalloc.erl b/lib/hipe/regalloc/hipe_ls_regalloc.erl
index 4276b8f968..7a00a0534a 100644
--- a/lib/hipe/regalloc/hipe_ls_regalloc.erl
+++ b/lib/hipe/regalloc/hipe_ls_regalloc.erl
@@ -722,7 +722,7 @@ is_free(R, Free) ->
is_free(R, Free, []).
is_free(R, [{R,_}|Rest], Acc) ->
- {true,lists:reverse(Acc)++Rest};
+ {true, lists:reverse(Acc, Rest)};
is_free(R, [X|Rs],Acc) ->
is_free(R, Rs, [X|Acc]);
is_free(_, [], _) ->
@@ -733,7 +733,7 @@ exists_free_register(Start, Regs) ->
exists_free_register(Start, [{Phys, Start0}|Rest], Acc)
when Start > Start0 ->
- {true, Phys, lists:reverse(Acc)++Rest};
+ {true, Phys, lists:reverse(Acc, Rest)};
exists_free_register(Start, [Free|Rest], Acc) ->
exists_free_register(Start, Rest, [Free|Acc]);
exists_free_register(_, [], _) ->
diff --git a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
index 5bad31ade9..0278a896d2 100644
--- a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
+++ b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
@@ -958,9 +958,9 @@ splits_2({Cols, NonCols, OldSpillCost}, L, SpillCost) ->
%% Merge two ordered sub-splits into one.
spillCostOrderedMerge(Spl1, [], Spl) ->
- lists:reverse(Spl) ++ Spl1;
+ lists:reverse(Spl, Spl1);
spillCostOrderedMerge([], Spl2, Spl) ->
- lists:reverse(Spl) ++ Spl2;
+ lists:reverse(Spl, Spl2);
spillCostOrderedMerge(Spl1, Spl2, Spl) ->
{_, _, SpillCost1} = hd(Spl1),
{_, _, SpillCost2} = hd(Spl2),
diff --git a/lib/hipe/rtl/hipe_icode2rtl.erl b/lib/hipe/rtl/hipe_icode2rtl.erl
index 034153a3cb..483d0b37f7 100644
--- a/lib/hipe/rtl/hipe_icode2rtl.erl
+++ b/lib/hipe/rtl/hipe_icode2rtl.erl
@@ -427,8 +427,6 @@ gen_type_test([X], Type, TrueLbl, FalseLbl, Pred, ConstTab) ->
hipe_rtl:mk_branch(X, eq, TmpF, TrueLbl, FalseLbl, Pred)], ConstTab};
cons ->
{hipe_tagscheme:test_cons(X, TrueLbl, FalseLbl, Pred), ConstTab};
- constant ->
- {hipe_tagscheme:test_constant(X, TrueLbl, FalseLbl, Pred), ConstTab};
fixnum ->
{hipe_tagscheme:test_fixnum(X, TrueLbl, FalseLbl, Pred), ConstTab};
float ->
@@ -439,6 +437,8 @@ gen_type_test([X], Type, TrueLbl, FalseLbl, Pred, ConstTab) ->
{hipe_tagscheme:test_integer(X, TrueLbl, FalseLbl, Pred), ConstTab};
list ->
{hipe_tagscheme:test_list(X, TrueLbl, FalseLbl, Pred), ConstTab};
+ map ->
+ {hipe_tagscheme:test_map(X, TrueLbl, FalseLbl, Pred), ConstTab};
nil ->
{hipe_tagscheme:test_nil(X, TrueLbl, FalseLbl, Pred), ConstTab};
number ->
diff --git a/lib/hipe/rtl/hipe_rtl.erl b/lib/hipe/rtl/hipe_rtl.erl
index 4bf4eb6bd7..bc61bec0bd 100644
--- a/lib/hipe/rtl/hipe_rtl.erl
+++ b/lib/hipe/rtl/hipe_rtl.erl
@@ -29,7 +29,7 @@
%% <li> {alu, Dst, Src1, Op, Src2} </li>
%% <li> {alub, Dst, Src1, Op, Src2, RelOp, TrueLabel, FalseLabel, P} </li>
%% <li> {branch, Src1, Src2, RelOp, TrueLabel, FalseLabel, P} </li>
-%% <li> {call, DsListt, Fun, ArgList, Type, Continuation, FailContinuation}
+%% <li> {call, DsListt, Fun, ArgList, Type, Continuation, FailContinuation, NormalContinuation}
%% Type is one of {local, remote, primop, closure} </li>
%% <li> {comment, Text} </li>
%% <li> {enter, Fun, ArgList, Type}
@@ -106,7 +106,7 @@
%% rtl_data_update/2,
%% rtl_var_range/1,
%% rtl_var_range_update/2,
- %% rtl_label_range/1,
+ rtl_label_range/1,
%% rtl_label_range_update/2,
rtl_info/1,
rtl_info_update/2]).
@@ -226,6 +226,7 @@
%% goto_label_update/2,
mk_call/6,
+ mk_call/7,
call_fun/1,
call_dstlist/1,
call_dstlist_update/2,
@@ -233,8 +234,10 @@
call_continuation/1,
call_fail/1,
call_type/1,
+ call_normal/1,
+ call_normal_update/2,
%% call_continuation_update/2,
- %% call_fail_update/2,
+ call_fail_update/2,
is_call/1,
mk_enter/3,
@@ -290,10 +293,13 @@
%% fconv_src_update/2,
%% is_fconv/1,
- %% mk_var/1,
+ mk_var/1,
+ mk_var/2,
mk_new_var/0,
is_var/1,
var_index/1,
+ var_liveness/1,
+ var_liveness_update/2,
%% change_vars_to_regs/1,
@@ -350,10 +356,15 @@
%% move_dst_update/2,
fixnumop_dst_update/2,
pp_instr/2,
- %% pp_arg/2,
+ %% Uber hack!
+ pp_var/2,
+ pp_reg/2,
+ pp_arg/2,
phi_arglist_update/2,
phi_redirect_pred/3]).
+-export([subst_uses_llvm/2]).
+
-export_type([alub_cond/0]).
%%
@@ -387,7 +398,7 @@ rtl_data(#rtl{data=Data}) -> Data.
%% rtl_data_update(Rtl, Data) -> Rtl#rtl{data=Data}.
%% rtl_var_range(#rtl{var_range=VarRange}) -> VarRange.
%% rtl_var_range_update(Rtl, VarRange) -> Rtl#rtl{var_range=VarRange}.
-%% rtl_label_range(#rtl{label_range=LabelRange}) -> LabelRange.
+rtl_label_range(#rtl{label_range=LabelRange}) -> LabelRange.
%% rtl_label_range_update(Rtl, LabelRange) -> Rtl#rtl{label_range=LabelRange}.
rtl_info(#rtl{info=Info}) -> Info.
rtl_info_update(Rtl, Info) -> Rtl#rtl{info=Info}.
@@ -643,6 +654,17 @@ is_goto(_) -> false.
%% call
%%
+%% LLVM: Call with normal continuation
+mk_call(DstList, Fun, ArgList, Continuation, FailContinuation,
+ NormalContinuation, Type) ->
+ case Type of
+ remote -> ok;
+ not_remote -> ok
+ end,
+ #call{dstlist=DstList, 'fun'=Fun, arglist=ArgList, type=Type,
+ continuation=Continuation, failcontinuation=FailContinuation,
+ normalcontinuation=NormalContinuation}.
+
mk_call(DstList, Fun, ArgList, Continuation, FailContinuation, Type) ->
case Type of
remote -> ok;
@@ -651,6 +673,10 @@ mk_call(DstList, Fun, ArgList, Continuation, FailContinuation, Type) ->
#call{dstlist=DstList, 'fun'=Fun, arglist=ArgList, type=Type,
continuation=Continuation,
failcontinuation=FailContinuation}.
+
+call_normal(#call{normalcontinuation=NormalContinuation}) -> NormalContinuation.
+call_normal_update(C, NewNormalContinuation) ->
+ C#call{normalcontinuation=NewNormalContinuation}.
call_dstlist(#call{dstlist=DstList}) -> DstList.
call_dstlist_update(C, NewDstList) -> C#call{dstlist=NewDstList}.
call_fun(#call{'fun'=Fun}) -> Fun.
@@ -853,11 +879,14 @@ reg_is_gcsafe(#rtl_reg{is_gc_safe=IsGcSafe}) -> IsGcSafe.
is_reg(#rtl_reg{}) -> true;
is_reg(_) -> false.
--record(rtl_var, {index :: non_neg_integer()}).
+-record(rtl_var, {index :: non_neg_integer(), liveness=live :: dead | live}).
mk_var(Num) when is_integer(Num), Num >= 0 -> #rtl_var{index=Num}.
+mk_var(Num, Liveness) when is_integer(Num), Num>=0 -> #rtl_var{index=Num, liveness=Liveness}.
mk_new_var() -> mk_var(hipe_gensym:get_next_var(rtl)).
var_index(#rtl_var{index=Index}) -> Index.
+var_liveness(#rtl_var{liveness=Liveness}) -> Liveness.
+var_liveness_update(RtlVar, Liveness) -> RtlVar#rtl_var{liveness=Liveness}.
is_var(#rtl_var{}) -> true;
is_var(_) -> false.
@@ -1077,6 +1106,131 @@ subst_uses(Subst, I) ->
switch_src_update(I, subst1(Subst, switch_src(I)))
end.
+subst_uses_llvm(Subst, I) ->
+ case I of
+ #alu{} ->
+ {NewSrc2, Subst1} = subst1_llvm(Subst, alu_src2(I)),
+ {NewSrc1, _ } = subst1_llvm(Subst1, alu_src1(I)),
+ I0 = alu_src1_update(I, NewSrc1),
+ alu_src2_update(I0, NewSrc2);
+ #alub{} ->
+ {NewSrc2, Subst1} = subst1_llvm(Subst, alub_src2(I)),
+ {NewSrc1, _ } = subst1_llvm(Subst1, alub_src1(I)),
+ I0 = alub_src1_update(I, NewSrc1),
+ alub_src2_update(I0, NewSrc2);
+ #branch{} ->
+ {NewSrc2, Subst1} = subst1_llvm(Subst, branch_src2(I)),
+ {NewSrc1, _ } = subst1_llvm(Subst1, branch_src1(I)),
+ I0 = branch_src1_update(I, NewSrc1),
+ branch_src2_update(I0, NewSrc2);
+ #call{} ->
+ case call_is_known(I) of
+ false ->
+ {NewFun, Subst1} = subst1_llvm(Subst, call_fun(I)),
+ {NewArgList, _} = subst_list_llvm(Subst1, call_arglist(I)),
+ I0 = call_fun_update(I, NewFun),
+ call_arglist_update(I0, NewArgList);
+ true ->
+ {NewArgList, _} = subst_list_llvm(Subst, call_arglist(I)),
+ call_arglist_update(I, NewArgList)
+ end;
+ #comment{} ->
+ I;
+ #enter{} ->
+ case enter_is_known(I) of
+ false ->
+ {NewFun, Subst1} = subst1_llvm(Subst, enter_fun(I)),
+ {NewArgList, _} = subst_list_llvm(Subst1, enter_arglist(I)),
+ I0 = enter_fun_update(I, NewFun),
+ enter_arglist_update(I0, NewArgList);
+ true ->
+ {NewArgList, _} = subst_list_llvm(Subst, enter_arglist(I)),
+ enter_arglist_update(I, NewArgList)
+ end;
+ #fconv{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, fconv_src(I)),
+ fconv_src_update(I, NewSrc);
+ #fixnumop{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, fixnumop_src(I)),
+ fixnumop_src_update(I, NewSrc);
+ #fload{} ->
+ {NewSrc, Subst1} = subst1_llvm(Subst, fload_src(I)),
+ {NewOffset, _ } = subst1_llvm(Subst1, fload_offset(I)),
+ I0 = fload_src_update(I, NewSrc),
+ fload_offset_update(I0, NewOffset);
+ #fmove{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, fmove_src(I)),
+ fmove_src_update(I, NewSrc);
+ #fp{} ->
+ {NewSrc2, Subst1} = subst1_llvm(Subst, fp_src2(I)),
+ {NewSrc1, _ } = subst1_llvm(Subst1, fp_src1(I)),
+ I0 = fp_src1_update(I, NewSrc1),
+ fp_src2_update(I0, NewSrc2);
+ #fp_unop{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, fp_unop_src(I)),
+ fp_unop_src_update(I, NewSrc);
+ #fstore{} ->
+ {NewSrc, Subst1} = subst1_llvm(Subst, fstore_src(I)),
+ {NewBase, Subst2} = subst1_llvm(Subst1, fstore_base(I)),
+ {NewOffset, _ } = subst1_llvm(Subst2, fstore_offset(I)),
+ I0 = fstore_src_update(I, NewSrc),
+ I1 = fstore_base_update(I0, NewBase),
+ fstore_offset_update(I1, NewOffset);
+ #goto{} ->
+ I;
+ #goto_index{} ->
+ I;
+ #gctest{} ->
+ {NewWords, _ } = subst1_llvm(Subst, gctest_words(I)),
+ gctest_words_update(I, NewWords);
+ #label{} ->
+ I;
+ #load{} ->
+ {NewSrc, Subst1} = subst1_llvm(Subst, load_src(I)),
+ {NewOffset, _ } = subst1_llvm(Subst1, load_offset(I)),
+ I0 = load_src_update(I, NewSrc),
+ load_offset_update(I0, NewOffset);
+ #load_address{} ->
+ I;
+ #load_atom{} ->
+ I;
+ #load_word_index{} ->
+ I;
+ #move{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, move_src(I)),
+ move_src_update(I, NewSrc);
+ #multimove{} ->
+ {NewSrcList, _} = subst_list_llvm(Subst, multimove_srclist(I)),
+ multimove_srclist_update(I, NewSrcList);
+ #phi{} ->
+ phi_argvar_subst(I, Subst);
+ #return{} ->
+ {NewVarList, _} = subst_list_llvm(Subst, return_varlist(I)),
+ return_varlist_update(I, NewVarList);
+ #store{} ->
+ {NewSrc, Subst1} = subst1_llvm(Subst, store_src(I)),
+ {NewBase, Subst2} = subst1_llvm(Subst1, store_base(I)),
+ {NewOffset, _ } = subst1_llvm(Subst2, store_offset(I)),
+ I0 = store_src_update(I, NewSrc),
+ I1 = store_base_update(I0, NewBase),
+ store_offset_update(I1, NewOffset);
+ #switch{} ->
+ {NewSrc, _ } = subst1_llvm(Subst, switch_src(I)),
+ switch_src_update(I, NewSrc)
+ end.
+
+subst_list_llvm(S,X) -> subst_list_llvm(S, lists:reverse(X), []).
+subst_list_llvm(S, [], Acc) -> {Acc, S};
+subst_list_llvm(S, [X|Xs], Acc) ->
+ {NewX, RestS} = subst1_llvm(S, X),
+ subst_list_llvm(RestS, Xs, [NewX|Acc]).
+
+subst1_llvm(A,B) -> subst1_llvm(A,B,[]).
+
+subst1_llvm([], X, Acc) -> {X, Acc};
+subst1_llvm([{X,Y}|Rs], X, Acc) -> {Y, Acc++Rs};
+subst1_llvm([R|Xs], X, Acc) -> subst1_llvm(Xs,X,[R|Acc]).
+
subst_defines(Subst, I)->
case I of
#alu{} ->
@@ -1614,7 +1768,11 @@ pp_var(Dev, Arg) ->
true ->
pp_hard_reg(Dev, var_index(Arg));
false ->
- io:format(Dev, "v~w", [var_index(Arg)])
+ io:format(Dev, "v~w", [var_index(Arg)]),
+ case var_liveness(Arg) of
+ dead -> io:format(Dev, "(dead)", []);
+ _ -> ok
+ end
end.
pp_arg(Dev, A) ->
diff --git a/lib/hipe/rtl/hipe_rtl.hrl b/lib/hipe/rtl/hipe_rtl.hrl
index 974e40f830..fbdf9ac524 100644
--- a/lib/hipe/rtl/hipe_rtl.hrl
+++ b/lib/hipe/rtl/hipe_rtl.hrl
@@ -28,7 +28,8 @@
-record(alu, {dst, src1, op, src2}).
-record(alub, {dst, src1, op, src2, 'cond', true_label, false_label, p}).
-record(branch, {src1, src2, 'cond', true_label, false_label, p}).
--record(call, {dstlist, 'fun', arglist, type, continuation, failcontinuation}).
+-record(call, {dstlist, 'fun', arglist, type, continuation,
+ failcontinuation, normalcontinuation = []}).
-record(comment, {text}).
-record(enter, {'fun', arglist, type}).
-record(fconv, {dst, src}).
diff --git a/lib/hipe/rtl/hipe_rtl_liveness.erl b/lib/hipe/rtl/hipe_rtl_liveness.erl
index 3cfada9d6c..0c4b6b2e11 100644
--- a/lib/hipe/rtl/hipe_rtl_liveness.erl
+++ b/lib/hipe/rtl/hipe_rtl_liveness.erl
@@ -34,7 +34,8 @@
-module(hipe_rtl_liveness).
-%% -define(LIVEOUT_NEEDED,true). % needed for liveness.inc below.
+%% -define(DEBUG_LIVENESS,true).
+-define(LIVEOUT_NEEDED,true). % needed for liveness.inc below.
-define(PRETTY_PRINT,false).
-include("hipe_rtl.hrl").
diff --git a/lib/hipe/rtl/hipe_tagscheme.erl b/lib/hipe/rtl/hipe_tagscheme.erl
index f1e8d1ef41..c27c682915 100644
--- a/lib/hipe/rtl/hipe_tagscheme.erl
+++ b/lib/hipe/rtl/hipe_tagscheme.erl
@@ -39,14 +39,13 @@
test_tuple/4, test_atom/4, test_bignum/4, test_pos_bignum/4,
test_any_pid/4, test_any_port/4,
test_ref/4, test_fun/4, test_fun2/5, test_matchstate/4,
- test_binary/4, test_bitstr/4, test_list/4,
- test_integer/4, test_number/4, test_constant/4, test_tuple_N/5]).
+ test_binary/4, test_bitstr/4, test_list/4, test_map/4,
+ test_integer/4, test_number/4, test_tuple_N/5]).
-export([realtag_fixnum/2, tag_fixnum/2, realuntag_fixnum/2, untag_fixnum/2]).
-export([test_two_fixnums/3, test_fixnums/4, unsafe_fixnum_add/3,
unsafe_fixnum_sub/3,
fixnum_gt/5, fixnum_lt/5, fixnum_ge/5, fixnum_le/5, fixnum_val/1,
- fixnum_mul/4,
- fixnum_addsub/5, fixnum_andorxor/4, fixnum_not/2,
+ fixnum_mul/4, fixnum_addsub/5, fixnum_andorxor/4, fixnum_not/2,
fixnum_bsr/3, fixnum_bsl/3]).
-export([unsafe_car/2, unsafe_cdr/2,
unsafe_constant_element/3, unsafe_update_element/3, element/6]).
@@ -113,13 +112,15 @@
-define(TAG_HEADER_EXTERNAL_PID, ((16#C bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)).
-define(TAG_HEADER_EXTERNAL_PORT,((16#D bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)).
-define(TAG_HEADER_EXTERNAL_REF, ((16#E bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)).
+-define(TAG_HEADER_MAP, ((16#F bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)).
-define(TAG_HEADER_MASK, 16#3F).
-define(HEADER_ARITY_OFFS, 6).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-mk_header(SZ,TAG) -> (SZ bsl ?HEADER_ARITY_OFFS) + TAG.
+mk_header(SZ, TAG) -> (SZ bsl ?HEADER_ARITY_OFFS) + TAG.
+
mk_arityval(SZ) -> mk_header(SZ, ?TAG_HEADER_ARITYVAL).
size_from_header(Sz, Header) ->
@@ -133,9 +134,9 @@ mk_var_header(Header, Size, Tag) ->
mk_fixnum(X) -> (X bsl ?TAG_IMMED1_SIZE) + ?TAG_IMMED1_SMALL.
-define(NIL, ((-1 bsl ?TAG_IMMED2_SIZE) bor ?TAG_IMMED2_NIL)).
-mk_nil() -> ?NIL.
-%% mk_atom(X) -> (X bsl ?TAG_IMMED2_SIZE) + ?TAG_IMMED2_ATOM.
-mk_non_value() -> ?THE_NON_VALUE.
+mk_nil() -> ?NIL.
+%% mk_atom(X) -> (X bsl ?TAG_IMMED2_SIZE) + ?TAG_IMMED2_ATOM.
+mk_non_value() -> ?THE_NON_VALUE.
-spec is_fixnum(integer()) -> boolean().
is_fixnum(N) when is_integer(N) ->
@@ -253,6 +254,15 @@ test_tuple_N(X, N, TrueLab, FalseLab, Pred) ->
hipe_rtl:mk_branch(Tmp, 'eq', hipe_rtl:mk_imm(mk_arityval(N)),
TrueLab, FalseLab, Pred)].
+test_map(X, TrueLab, FalseLab, Pred) ->
+ Tmp = hipe_rtl:mk_new_reg_gcsafe(),
+ HalfTrueLab = hipe_rtl:mk_new_label(),
+ MapMask = ?TAG_HEADER_MASK,
+ [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred),
+ HalfTrueLab,
+ get_header(Tmp, X),
+ mask_and_compare(Tmp, MapMask, ?TAG_HEADER_MAP, TrueLab, FalseLab, Pred)].
+
test_ref(X, TrueLab, FalseLab, Pred) ->
Hdr = hipe_rtl:mk_new_reg_gcsafe(),
Tag = hipe_rtl:mk_new_reg_gcsafe(),
@@ -405,17 +415,6 @@ test_number(X, TrueLab, FalseLab, Pred) ->
hipe_rtl:mk_branch(Tmp, 'eq', hipe_rtl:mk_imm(HeaderFlonum),
TrueLab, FalseLab, Pred)].
-%% CONS, NIL, and TUPLE are not constants, everything else is
-test_constant(X, TrueLab, FalseLab, Pred) ->
- Lab1 = hipe_rtl:mk_new_label(),
- Lab2 = hipe_rtl:mk_new_label(),
- Pred1 = 1-Pred,
- [test_cons(X, FalseLab, hipe_rtl:label_name(Lab1), Pred1),
- Lab1,
- test_nil(X, FalseLab, hipe_rtl:label_name(Lab2), Pred1),
- Lab2,
- test_tuple(X, FalseLab, TrueLab, Pred1)].
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
tag_fixnum(DestVar, SrcReg) ->
diff --git a/lib/hipe/sparc/hipe_sparc_assemble.erl b/lib/hipe/sparc/hipe_sparc_assemble.erl
index b534fe20ec..68a4e1b349 100644
--- a/lib/hipe/sparc/hipe_sparc_assemble.erl
+++ b/lib/hipe/sparc/hipe_sparc_assemble.erl
@@ -45,8 +45,8 @@ assemble(CompiledCode, Closures, Exports, Options) ->
print("Total num bytes=~w\n", [CodeSize], Options),
%%
SC = hipe_pack_constants:slim_constmap(ConstMap),
- DataRelocs = mk_data_relocs(RefsFromConsts, LabelMap),
- SSE = slim_sorted_exportmap(ExportMap,Closures,Exports),
+ DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
+ SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap,Closures,Exports),
SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
Bin = term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC},
ConstAlign, ConstSize,
@@ -222,7 +222,7 @@ do_pseudo_set(I, MFA, ConstMap) ->
%%% end,
%%% {load_address, {Tag,untag_mfa_or_prim(MFAorPrim)}};
{Label,constant} ->
- ConstNo = find_const({MFA,Label}, ConstMap),
+ ConstNo = hipe_pack_constants:find_const({MFA,Label}, ConstMap),
{load_address, {constant,ConstNo}};
{Label,closure} ->
{load_address, {closure,Label}};
@@ -507,37 +507,6 @@ px({pred,Pred}) -> % XXX: use pt/pn throughout entire backend
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-mk_data_relocs(RefsFromConsts, LabelMap) ->
- lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])).
-
-mk_data_relocs([{MFA,Labels} | Rest], LabelMap, Acc) ->
- Map = [case Label of
- {L,Pos} ->
- Offset = find({MFA,L}, LabelMap),
- {Pos,Offset};
- {sorted,Base,OrderedLabels} ->
- {sorted, Base, [begin
- Offset = find({MFA,L}, LabelMap),
- {Order, Offset}
- end
- || {L,Order} <- OrderedLabels]}
- end
- || Label <- Labels],
- %% msg("Map: ~w Map\n",[Map]),
- mk_data_relocs(Rest, LabelMap, [Map,Acc]);
-mk_data_relocs([],_,Acc) -> Acc.
-
-find({_MFA,_L} = MFAL, LabelMap) ->
- gb_trees:get(MFAL, LabelMap).
-
-slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) ->
- IsClosure = lists:member({M,F,A}, Closures),
- IsExported = is_exported(F, A, Exports),
- [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)];
-slim_sorted_exportmap([],_,_) -> [].
-
-is_exported(F, A, Exports) -> lists:member({F,A}, Exports).
-
%%%
%%% Assembly listing support (pp_asm option).
%%%
@@ -575,14 +544,3 @@ fill_spaces(N) when N > 0 ->
fill_spaces(N-1);
fill_spaces(0) ->
[].
-
-%%%
-%%% Lookup a constant in a ConstMap.
-%%%
-
-find_const({MFA,Label},[{pcm_entry,MFA,Label,ConstNo,_,_,_}|_]) ->
- ConstNo;
-find_const(N,[_|R]) ->
- find_const(N,R);
-find_const(C,[]) ->
- ?EXIT({constant_not_found,C}).
diff --git a/lib/hipe/test/Makefile b/lib/hipe/test/Makefile
index cedb150b5d..009f503abb 100644
--- a/lib/hipe/test/Makefile
+++ b/lib/hipe/test/Makefile
@@ -8,6 +8,11 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES= \
hipe_SUITE
+# .erl files for these modules are automatically generated
+GEN_MODULES= \
+ bs_SUITE \
+ maps_SUITE
+
ERL_FILES= $(MODULES:%=%.erl)
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
@@ -40,6 +45,8 @@ EBIN = .
make_emakefile:
$(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
> $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(GEN_MODULES) \
+ >> $(EMAKEFILE)
$(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \
>> $(EMAKEFILE)
diff --git a/lib/hipe/test/hipe_testsuite_driver.erl b/lib/hipe/test/hipe_testsuite_driver.erl
index c8fdf1600c..5f05a716bc 100644
--- a/lib/hipe/test/hipe_testsuite_driver.erl
+++ b/lib/hipe/test/hipe_testsuite_driver.erl
@@ -85,7 +85,7 @@ list_testcases(Dirname) ->
list_dir(Dir, Extension, Dirs) ->
case file:list_dir(Dir) of
- {error, _} = Error-> Error;
+ {error, _} = Error -> Error;
{ok, Filenames} ->
FullFilenames = [filename:join(Dir, F) || F <- Filenames],
Matches1 = case Dirs of
@@ -173,10 +173,29 @@ run(TestCase, Dir, _OutDir) ->
%% end, DataFiles),
%% try
ok = TestCase:test(),
- HiPEOpts = try TestCase:hipe_options() catch _:_ -> [] end,
+ HiPEOpts = try TestCase:hipe_options() catch error:undef -> [] end,
{ok, TestCase} = hipe:c(TestCase, HiPEOpts),
- ok = TestCase:test().
+ ok = TestCase:test(),
+ case is_llvm_opt_available() of
+ true ->
+ {ok, TestCase} = hipe:c(TestCase, [to_llvm|HiPEOpts]),
+ ok = TestCase:test();
+ false -> ok
+ end.
%% after
%% lists:foreach(fun (DF) -> ok end, % = file:delete(DF) end,
%% [filename:join(OutDir, D) || D <- DataFiles])
%% end.
+
+
+%% This function, which is supposed to check whether the right LLVM
+%% infrastructure is available, should be probably written in a better
+%% and more portable way and moved to the hipe application.
+
+is_llvm_opt_available() ->
+ OptStr = os:cmd("opt -version"),
+ SubStr = "LLVM version ", N = length(SubStr),
+ case string:str(OptStr, SubStr) of
+ 0 -> false;
+ S -> P = S + N, string:sub_string(OptStr, P, P + 2) >= "3.4"
+ end.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_build_and_match_aliasing.erl b/lib/hipe/test/maps_SUITE_data/maps_build_and_match_aliasing.erl
new file mode 100644
index 0000000000..14d8320cdf
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_build_and_match_aliasing.erl
@@ -0,0 +1,20 @@
+-module(maps_build_and_match_aliasing).
+-export([test/0]).
+
+test() ->
+ M1 = id(#{a=>1,b=>2,c=>3,d=>4}),
+ #{c:=C1=_=_=C2} = M1,
+ true = C1 =:= C2,
+ #{a:=A,a:=A,a:=A,b:=B,b:=B} = M1,
+ #{a:=A,a:=A,a:=A,b:=B,b:=B,b:=2} = M1,
+ #{a:=A=1,a:=A,a:=A,b:=B=2,b:=B,b:=2} = M1,
+ #{c:=C1, c:=_, c:=3, c:=_, c:=C2} = M1,
+ #{c:=C=_=3=_=C} = M1,
+
+ M2 = id(#{"a"=>1,"b"=>2,"c"=>3,"d"=>4}),
+ #{"a":=A2,"a":=A2,"a":=A2,"b":=B2,"b":=B2,"b":=2} = M2,
+ #{"a":=_,"a":=_,"a":=_,"b":=_,"b":=_,"b":=2} = M2,
+ ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_build_and_match_empty_val.erl b/lib/hipe/test/maps_SUITE_data/maps_build_and_match_empty_val.erl
new file mode 100644
index 0000000000..2abfa4e5b3
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_build_and_match_empty_val.erl
@@ -0,0 +1,17 @@
+-module(maps_build_and_match_empty_val).
+-export([test/0]).
+
+test() ->
+ F = fun(#{ "hi":=_,{1,2}:=_,1337:=_}) -> ok end,
+ ok = F(id(#{"hi"=>ok,{1,2}=>ok,1337=>ok})),
+
+ %% error case
+ case (catch (F(id(#{"hi"=>ok})))) of
+ {'EXIT',{function_clause,_}} -> ok;
+ {'EXIT', {{case_clause,_},_}} -> {comment,inlined};
+ Other ->
+ test_server:fail({no_match, Other})
+ end.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_build_and_match_literals.erl b/lib/hipe/test/maps_SUITE_data/maps_build_and_match_literals.erl
new file mode 100644
index 0000000000..dc2c63fab2
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_build_and_match_literals.erl
@@ -0,0 +1,40 @@
+-module(maps_build_and_match_literals).
+-export([test/0]).
+
+test() ->
+ #{} = id(#{}),
+ #{1:=a} = id(#{1=>a}),
+ #{1:=a,2:=b} = id(#{1=>a,2=>b}),
+ #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}),
+ #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}),
+ #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} =
+ id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}),
+
+ #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}),
+
+ #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =)
+
+ #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} =
+ id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}),
+
+ M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first},
+ M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} =
+ id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}),
+
+ %% nil key
+ #{[]:=ok,1:=2} = id(#{[]=>ok,1=>2}),
+
+ %% error case
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))),
+ {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))),
+ ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_build_and_match_over_alloc.erl b/lib/hipe/test/maps_SUITE_data/maps_build_and_match_over_alloc.erl
new file mode 100644
index 0000000000..dae6f64e5f
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_build_and_match_over_alloc.erl
@@ -0,0 +1,16 @@
+-module(maps_build_and_match_over_alloc).
+-export([test/0]).
+
+test() ->
+ Ls = id([1,2,3]),
+ V0 = [a|Ls],
+ M0 = id(#{ "a" => V0 }),
+ #{ "a" := V1 } = M0,
+ V2 = id([c|Ls]),
+ M2 = id(#{ "a" => V2 }),
+ #{ "a" := V3 } = M2,
+ {[a,1,2,3],[c,1,2,3]} = id({V1,V3}),
+ ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_build_and_match_val.erl b/lib/hipe/test/maps_SUITE_data/maps_build_and_match_val.erl
new file mode 100644
index 0000000000..284f69e06c
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_build_and_match_val.erl
@@ -0,0 +1,23 @@
+-module(maps_build_and_match_val).
+-export([test/0]).
+
+test() ->
+ F = fun
+ (#{ "hi" := first, v := V}) -> {1,V};
+ (#{ "hi" := second, v := V}) -> {2,V}
+ end,
+
+
+ {1,"hello"} = F(id(#{"hi"=>first,v=>"hello"})),
+ {2,"second"} = F(id(#{"hi"=>second,v=>"second"})),
+
+ %% error case
+ case (catch (F(id(#{"hi"=>ok})))) of
+ {'EXIT',{function_clause,_}} -> ok;
+ {'EXIT', {{case_clause,_},_}} -> {comment,inlined};
+ Other ->
+ test_server:fail({no_match, Other})
+ end.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_expand_map_update.erl b/lib/hipe/test/maps_SUITE_data/maps_expand_map_update.erl
new file mode 100644
index 0000000000..df0f77ea47
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_expand_map_update.erl
@@ -0,0 +1,7 @@
+-module(maps_expand_map_update).
+-export([test/0]).
+
+test() ->
+ M = #{<<"hello">> => <<"world">>}#{<<"hello">> := <<"les gens">>},
+ #{<<"hello">> := <<"les gens">>} = M,
+ ok.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_export.erl b/lib/hipe/test/maps_SUITE_data/maps_export.erl
new file mode 100644
index 0000000000..4d43fc96ed
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_export.erl
@@ -0,0 +1,11 @@
+-module(maps_export).
+-export([test/0]).
+
+test() ->
+ Raclette = id(#{}),
+ case brie of brie -> Fromage = Raclette end,
+ Raclette = Fromage#{},
+ ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_get_map_elements.erl b/lib/hipe/test/maps_SUITE_data/maps_get_map_elements.erl
new file mode 100644
index 0000000000..b2d749796a
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_get_map_elements.erl
@@ -0,0 +1,23 @@
+%% -*- erlang-indent-level: 2 -*-
+%%-------------------------------------------------------------------------
+-module(maps_get_map_elements).
+
+-export([test/0]).
+
+test() ->
+ {A, B} = id({"hej", <<123>>}),
+ Map = maps:from_list([{a, A}, {b, B}]),
+ #{a := A, b := B} = id(Map),
+ false = test_pattern(Map),
+ true = test_pattern(#{b => 1, a => "hej"}),
+ case Map of
+ #{a := C, b := <<124>>} -> yay;
+ _ -> C = B, nay
+ end,
+ C = id(B),
+ ok.
+
+id(X) -> X.
+
+test_pattern(#{a := _, b := 1}) -> true;
+test_pattern(#{}) -> false.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_guard_bifs.erl b/lib/hipe/test/maps_SUITE_data/maps_guard_bifs.erl
new file mode 100644
index 0000000000..61a0eaa1e7
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_guard_bifs.erl
@@ -0,0 +1,31 @@
+-module(maps_guard_bifs).
+-export([test/0]).
+
+test() ->
+ true = map_guard_empty(),
+ true = map_guard_empty_2(),
+ true = map_guard_head(#{a=>1}),
+ false = map_guard_head([]),
+ true = map_guard_body(#{a=>1}),
+ false = map_guard_body({}),
+ true = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }),
+ false = map_guard_pattern("list"),
+ true = map_guard_tautology(),
+ true = map_guard_ill_map_size(),
+ ok.
+
+map_guard_empty() when is_map(#{}); false -> true.
+
+map_guard_empty_2() when true; #{} andalso false -> true.
+
+map_guard_head(M) when is_map(M) -> true;
+map_guard_head(_) -> false.
+
+map_guard_body(M) -> is_map(M).
+
+map_guard_pattern(#{}) -> true;
+map_guard_pattern(_) -> false.
+
+map_guard_tautology() when #{} =:= #{}; true -> true.
+
+map_guard_ill_map_size() when true; map_size(0) -> true.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_guard_fun.erl b/lib/hipe/test/maps_SUITE_data/maps_guard_fun.erl
new file mode 100644
index 0000000000..9f6eb3a04e
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_guard_fun.erl
@@ -0,0 +1,36 @@
+-module(maps_guard_fun).
+-export([test/0]).
+
+test() ->
+ F1 = fun
+ (#{s:=v,v:=V}) -> {v,V};
+ (#{s:=t,v:={V,V}}) -> {t,V};
+ (#{s:=l,v:=[V,V]}) -> {l,V}
+ end,
+
+ F2 = fun
+ (#{s:=T,v:={V,V}}) -> {T,V};
+ (#{s:=T,v:=[V,V]}) -> {T,V};
+ (#{s:=T,v:=V}) -> {T,V}
+ end,
+ V = <<"hi">>,
+
+ {v,V} = F1(#{s=>v,v=>V}),
+ {t,V} = F1(#{s=>t,v=>{V,V}}),
+ {l,V} = F1(#{s=>l,v=>[V,V]}),
+
+ {v,V} = F2(#{s=>v,v=>V}),
+ {t,V} = F2(#{s=>t,v=>{V,V}}),
+ {l,V} = F2(#{s=>l,v=>[V,V]}),
+
+ %% error case
+ case (catch F1(#{s=>none,v=>none})) of
+ {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} -> ok;
+ {'EXIT', {function_clause,[{?MODULE,_,1,[#{s:=none,v:=none}]}|_]}} -> ok;
+ {'EXIT', {function_clause,[Frame|_]}}
+ when is_tuple(Frame), element(1, Frame) =:= ?MODULE ->
+ test_server:comment("Unexpected trace format, probably using HiPE");
+ {'EXIT', {{case_clause,_},_}} -> {comment,inlined};
+ Other ->
+ test_server:fail({no_match, Other})
+ end.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_guard_receive.erl b/lib/hipe/test/maps_SUITE_data/maps_guard_receive.erl
new file mode 100644
index 0000000000..f84ba19c86
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_guard_receive.erl
@@ -0,0 +1,54 @@
+-module(maps_guard_receive).
+-export([test/0]).
+
+test() ->
+ M0 = #{ id => 0 },
+ Pid = spawn_link(fun() -> guard_receive_loop() end),
+ Big = 36893488147419103229,
+ B1 = <<"some text">>,
+ B2 = <<"was appended">>,
+ B3 = <<B1/binary, B2/binary>>,
+
+ #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=2, res:=26} = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}),
+ #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}),
+ #{id:=4, res:=4} = M4 = call(Pid, M3#{op=>add,in=>{1,3}}),
+ #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}),
+ #{id:=6, res:=B3} = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}),
+ #{id:=7, res:=4} = _ = call(Pid, M6#{op=>add,in=>{1,3}}),
+
+
+ %% update old maps and check id update
+ #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}),
+ #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}),
+
+ %% cleanup
+ done = call(Pid, done),
+ ok.
+
+call(Pid, M) ->
+ Pid ! {self(), M}, receive {Pid, Res} -> Res end.
+
+guard_receive_loop() ->
+ receive
+ {Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) ->
+ Pid ! {self(), M#{ id=>Id+1, res=><<X/binary,Y/binary>>}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=add, in:={X,Y}}} ->
+ Pid ! {self(), #{ id=>Id+1, res=>X+Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=sub, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X-Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X div Y}},
+ guard_receive_loop();
+ {Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} ->
+ Pid ! {self(), M#{ id=>Id+1, res=>X * Y}},
+ guard_receive_loop();
+ {Pid, done} ->
+ Pid ! {self(), done};
+ {Pid, Other} ->
+ Pid ! {error, Other},
+ guard_receive_loop()
+ end.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_guard_sequence.erl b/lib/hipe/test/maps_SUITE_data/maps_guard_sequence.erl
new file mode 100644
index 0000000000..4eb18dcea1
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_guard_sequence.erl
@@ -0,0 +1,35 @@
+-module(maps_guard_sequence).
+-export([test/0]).
+
+test() ->
+ {1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}),
+ {2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}),
+ {3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}),
+ {4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}),
+ {5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}),
+
+ {1,M1} = map_guard_sequence_2(M1 = id(#{a=>3})),
+ {2,M2} = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})),
+ {3,gg,M3} = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})),
+ {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})),
+ {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})),
+
+ %% error case
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})),
+ {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})),
+ ok.
+
+map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val};
+map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}.
+
+map_guard_sequence_2(#{ a:=3 }=M) -> {1, M};
+map_guard_sequence_2(#{ a:=4 }=M) -> {2, M};
+map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M};
+map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M};
+map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_guard_update.erl b/lib/hipe/test/maps_SUITE_data/maps_guard_update.erl
new file mode 100644
index 0000000000..254c1c2984
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_guard_update.erl
@@ -0,0 +1,14 @@
+-module(maps_guard_update).
+-export([test/0]).
+
+test() ->
+ error = map_guard_update(#{},#{}),
+ first = map_guard_update(#{}, #{x=>first}),
+ second = map_guard_update(#{y=>old}, #{x=>second,y=>old}),
+ third = map_guard_update(#{x=>old,y=>old}, #{x=>third,y=>old}),
+ ok.
+
+map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first;
+map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second;
+map_guard_update(M1, M2) when M1#{x:=third} =:= M2 -> third;
+map_guard_update(_, _) -> error.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_has_map_fields.erl b/lib/hipe/test/maps_SUITE_data/maps_has_map_fields.erl
new file mode 100644
index 0000000000..61653aa519
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_has_map_fields.erl
@@ -0,0 +1,46 @@
+%% -*- erlang-indent-level: 2 -*-
+%%-------------------------------------------------------------------------
+-module(maps_has_map_fields).
+
+-export([test/0]).
+
+test() ->
+ false = has_a_field(#{}),
+ false = has_a_field(#{b => 2}),
+ true = has_a_field(#{a => 3}),
+ true = has_a_field(#{b => c, a => false}),
+
+ false = has_a_b_field(#{a => true}),
+ false = has_a_b_field(#{b => a}),
+ true = has_a_b_field(#{a => 1, b => 2}),
+ true = has_a_b_field(#{b => 3, a => 4}),
+
+ false = has_binary_field(#{}),
+ false = has_binary_field(#{#{} => yay}),
+ true = has_binary_field(#{<<"true">> => false}),
+
+ false = has_binary_but_no_map_field(#{}),
+ false = has_map_but_no_binary_field(#{}),
+ false = has_binary_but_no_map_field(#{#{} => 1}),
+ false = has_map_but_no_binary_field(#{<<"true">> => true}),
+ true = has_binary_but_no_map_field(#{<<"true">> => false}),
+ true = has_map_but_no_binary_field(#{#{} => 1}),
+ false = has_binary_but_no_map_field(#{<<"true">> => true, #{} => 1}),
+ false = has_map_but_no_binary_field(#{<<"true">> => true, #{} => 1}),
+ ok.
+
+has_a_field(#{a := _}) -> true;
+has_a_field(#{}) -> false.
+
+has_a_b_field(#{a := _, b := _}) -> true;
+has_a_b_field(#{}) -> false.
+
+has_binary_field(#{<<"true">> := _}) -> true;
+has_binary_field(#{}) -> false.
+
+has_map_but_no_binary_field(#{<<"true">> := _}) -> false;
+has_map_but_no_binary_field(#{} = M) -> maps:is_key(#{}, M).
+
+has_binary_but_no_map_field(#{<<"true">> := _} = M) ->
+ not maps:is_key(#{}, M);
+has_binary_but_no_map_field(#{}) -> false.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_is_map.erl b/lib/hipe/test/maps_SUITE_data/maps_is_map.erl
new file mode 100644
index 0000000000..e84f4b8c44
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_is_map.erl
@@ -0,0 +1,24 @@
+%% -*- erlang-indent-level: 2 -*-
+%%-------------------------------------------------------------------------
+-module(maps_is_map).
+
+-export([test/0]).
+
+test() ->
+ true = test_is_map(#{}),
+ false = test_is_map(<<"hej">>),
+ true = test_is_map_guard(#{a => b}),
+ false = test_is_map_guard(3),
+ true = test_is_map_with_binary_guard(#{"a" => <<"b">>}),
+ false = test_is_map_with_binary_guard(12),
+ ok.
+
+test_is_map(X) ->
+ is_map(X).
+
+test_is_map_guard(Map) when is_map(Map) -> true;
+test_is_map_guard(_) -> false.
+
+test_is_map_with_binary_guard(B) when is_binary(B) -> false;
+test_is_map_with_binary_guard(#{}) -> true;
+test_is_map_with_binary_guard(_) -> false.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_list_comprehension.erl b/lib/hipe/test/maps_SUITE_data/maps_list_comprehension.erl
new file mode 100644
index 0000000000..ad2c726d65
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_list_comprehension.erl
@@ -0,0 +1,6 @@
+-module(maps_list_comprehension).
+-export([test/0]).
+
+test() ->
+ [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]],
+ ok.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_map_size.erl b/lib/hipe/test/maps_SUITE_data/maps_map_size.erl
new file mode 100644
index 0000000000..25c8e5d4c7
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_map_size.erl
@@ -0,0 +1,29 @@
+-module(maps_map_size).
+-export([test/0]).
+
+test() ->
+ 0 = map_size(id(#{})),
+ 1 = map_size(id(#{a=>1})),
+ 1 = map_size(id(#{a=>"wat"})),
+ 2 = map_size(id(#{a=>1, b=>2})),
+ 3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})),
+
+ true = map_is_size(#{a=>1}, 1),
+ true = map_is_size(#{a=>1, a=>2}, 1),
+ M = #{ "a" => 1, "b" => 2},
+ true = map_is_size(M, 2),
+ false = map_is_size(M, 3),
+ true = map_is_size(M#{ "a" => 2}, 2),
+ false = map_is_size(M#{ "c" => 2}, 2),
+
+ %% Error cases.
+ {'EXIT',{badarg,_}} = (catch map_size([])),
+ {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)),
+ {'EXIT',{badarg,_}} = (catch map_size(1)),
+ ok.
+
+map_is_size(M,N) when map_size(M) =:= N -> true;
+map_is_size(_,_) -> false.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_map_sort_literals.erl b/lib/hipe/test/maps_SUITE_data/maps_map_sort_literals.erl
new file mode 100644
index 0000000000..31abf15d49
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_map_sort_literals.erl
@@ -0,0 +1,41 @@
+-module(maps_map_sort_literals).
+-export([test/0]).
+
+test() ->
+ % test relation
+
+ %% size order
+ true = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}),
+ true = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}),
+ false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}),
+
+ %% key order
+ true = id(#{ a => 1 }) < id(#{ b => 1}),
+ false = id(#{ b => 1 }) < id(#{ a => 1}),
+ true = id(#{ a => 1, b => 1, c => 1 }) < id(#{ b => 1, c => 1, d => 1}),
+ true = id(#{ b => 1, c => 1, d => 1 }) > id(#{ a => 1, b => 1, c => 1}),
+ true = id(#{ c => 1, b => 1, a => 1 }) < id(#{ b => 1, c => 1, d => 1}),
+ true = id(#{ "a" => 1 }) < id(#{ <<"a">> => 1}),
+ false = id(#{ <<"a">> => 1 }) < id(#{ "a" => 1}),
+ false = id(#{ 1 => 1 }) < id(#{ 1.0 => 1}),
+ false = id(#{ 1.0 => 1 }) < id(#{ 1 => 1}),
+
+ %% value order
+ true = id(#{ a => 1 }) < id(#{ a => 2}),
+ false = id(#{ a => 2 }) < id(#{ a => 1}),
+ false = id(#{ a => 2, b => 1 }) < id(#{ a => 1, b => 3}),
+ true = id(#{ a => 1, b => 1 }) < id(#{ a => 1, b => 3}),
+
+ true = id(#{ "a" => "hi", b => 134 }) == id(#{ b => 134,"a" => "hi"}),
+
+ %% lists:sort
+
+ SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}],
+ [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs),
+ [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)),
+
+ ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_match_and_update_literals.erl b/lib/hipe/test/maps_SUITE_data/maps_match_and_update_literals.erl
new file mode 100644
index 0000000000..29a6a29290
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_match_and_update_literals.erl
@@ -0,0 +1,24 @@
+-module(maps_match_and_update_literals).
+-export([test/0]).
+
+test() ->
+ Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1},
+ #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [
+ {1,2},{3,4},{5,6},{7,8}
+ ]),
+ M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat}),
+ M1 = id(#{}),
+ M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
+ 4 => number, 18446744073709551629 => wat},
+ M0 = M2,
+
+ #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number },
+ ok.
+
+loop_match_and_update_literals_x_q(Map, []) -> Map;
+loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) ->
+ loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_put_map_assoc.erl b/lib/hipe/test/maps_SUITE_data/maps_put_map_assoc.erl
new file mode 100644
index 0000000000..72ac9ce078
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_put_map_assoc.erl
@@ -0,0 +1,23 @@
+%% -*- erlang-indent-level: 2 -*-
+%%-------------------------------------------------------------------------
+-module(maps_put_map_assoc).
+
+-export([test/0]).
+
+test() ->
+ true = assoc_guard(#{}),
+ false = assoc_guard(not_a_map),
+ #{a := true} = assoc_update(#{}),
+ {'EXIT', {badarg, [{?MODULE, assoc_update, 1, _}|_]}}
+ = (catch assoc_update(not_a_map)),
+ ok = assoc_guard_clause(#{}),
+ {'EXIT', {function_clause, [{?MODULE, assoc_guard_clause, _, _}|_]}}
+ = (catch assoc_guard_clause(not_a_map)),
+ ok.
+
+assoc_guard(M) when is_map(M#{a => b}) -> true;
+assoc_guard(_) -> false.
+
+assoc_update(M) -> M#{a => true}.
+
+assoc_guard_clause(M) when is_map(M#{a => 3}) -> ok.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_put_map_exact.erl b/lib/hipe/test/maps_SUITE_data/maps_put_map_exact.erl
new file mode 100644
index 0000000000..1cfcd80180
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_put_map_exact.erl
@@ -0,0 +1,28 @@
+%% -*- erlang-indent-level: 2 -*-
+%%-------------------------------------------------------------------------
+-module(maps_put_map_exact).
+
+-export([test/0]).
+
+test() ->
+ false = exact_guard(#{b => a}),
+ false = exact_guard(not_a_map),
+ true = exact_guard(#{a => false}),
+ #{a := true} = exact_update(#{a => false}),
+ {'EXIT', {badarg, [{?MODULE, exact_update, 1, _}|_]}}
+ = (catch exact_update(not_a_map)),
+ {'EXIT', {badarg, [{?MODULE, exact_update, 1, _}|_]}}
+ = (catch exact_update(#{})),
+ ok = exact_guard_clause(#{a => yes}),
+ {'EXIT', {function_clause, [{?MODULE, exact_guard_clause, _, _}|_]}}
+ = (catch exact_guard_clause(#{})),
+ {'EXIT', {function_clause, [{?MODULE, exact_guard_clause, _, _}|_]}}
+ = (catch exact_guard_clause(not_a_map)),
+ ok.
+
+exact_guard(M) when is_map(M#{a := b}) -> true;
+exact_guard(_) -> false.
+
+exact_update(M) -> M#{a := true}.
+
+exact_guard_clause(M) when is_map(M#{a := 42}) -> ok.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_update_assoc.erl b/lib/hipe/test/maps_SUITE_data/maps_update_assoc.erl
new file mode 100644
index 0000000000..cc7c1353de
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_update_assoc.erl
@@ -0,0 +1,22 @@
+-module(maps_update_assoc).
+-export([test/0]).
+
+test() ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},
+
+ M2 = M0#{3.0=>new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0:=wrong,3.0=>new},
+
+ %% Errors cases.
+ BadMap = id(badmap),
+ {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}),
+
+ ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_update_exact.erl b/lib/hipe/test/maps_SUITE_data/maps_update_exact.erl
new file mode 100644
index 0000000000..6e5acb3283
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_update_exact.erl
@@ -0,0 +1,32 @@
+-module(maps_update_exact).
+-export([test/0]).
+
+test() ->
+ M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),
+
+ M1 = M0#{1:=42,2:=100,4:=[a,b,c]},
+ #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
+ M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]},
+
+ M2 = M0#{3.0:=new},
+ #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
+ M2 = M0#{3.0=>wrong,3.0:=new},
+ true = M2 =/= M0#{3=>right,3.0:=new},
+ #{ 3 := right, 3.0 := new } = M0#{3=>right,3.0:=new},
+
+ M3 = id(#{ 1 => val}),
+ #{1 := update2,1.0 := new_val4} = M3#{
+ 1.0 => new_val1, 1 := update, 1=> update3,
+ 1 := update2, 1.0 := new_val2, 1.0 => new_val3,
+ 1.0 => new_val4 },
+
+ %% Errors cases.
+ {'EXIT',{badarg,_}} = (catch ((id(nil))#{ a := b })),
+ {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}),
+ {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}),
+ {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),
+ ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_update_literals.erl b/lib/hipe/test/maps_SUITE_data/maps_update_literals.erl
new file mode 100644
index 0000000000..87aea3d8e1
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_update_literals.erl
@@ -0,0 +1,13 @@
+-module(maps_update_literals).
+-export([test/0]).
+
+test() ->
+ Map = #{x=>1,y=>2,z=>3,q=>4},
+ #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [
+ {"a","1"},{"b","2"},{"c","3"},{"d","4"}
+ ]),
+ ok.
+
+loop_update_literals_x_q(Map, []) -> Map;
+loop_update_literals_x_q(Map, [{X,Q}|Vs]) ->
+ loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs).
diff --git a/lib/hipe/test/maps_SUITE_data/maps_update_map_expressions.erl b/lib/hipe/test/maps_SUITE_data/maps_update_map_expressions.erl
new file mode 100644
index 0000000000..181e3f18f7
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_update_map_expressions.erl
@@ -0,0 +1,32 @@
+-module(maps_update_map_expressions).
+-export([test/0]).
+
+test() ->
+ M = maps:new(),
+ X = id(fondue),
+ M1 = #{ a := 1 } = M#{a => 1},
+ #{ b := {X} } = M1#{ a := 1, b => {X} },
+
+ #{ b := 2 } = (maps:new())#{ b => 2 },
+
+ #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 },
+ #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 },
+
+ %% Test need to be in a fun.
+ %% This tests that let expr optimisation in sys_core_fold
+ %% covers maps correctly.
+ F = fun() ->
+ M0 = id(#{ "a" => [1,2,3] }),
+ #{ "a" := _ } = M0,
+ M0#{ "a" := b }
+ end,
+
+ #{ "a" := b } = F(),
+
+ %% Error cases, FIXME: should be 'badmap'?
+ {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }),
+ {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }),
+ ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_update_values.erl b/lib/hipe/test/maps_SUITE_data/maps_update_values.erl
new file mode 100644
index 0000000000..bbad5ac19e
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_update_values.erl
@@ -0,0 +1,28 @@
+-module(maps_update_values).
+-export([test/0]).
+
+test() ->
+ V0 = id(1337),
+ M0 = #{ a => 1, val => V0},
+ V1 = get_val(M0),
+ M1 = M0#{ val := [V0,V1], "wazzup" => 42 },
+ [1337, {some_val, 1337}] = get_val(M1),
+
+ N = 110,
+ List = [{[I,1,2,3,I],{1,2,3,"wat",I}}|| I <- lists:seq(1,N)],
+
+ {_,_,#{val2 := {1,2,3,"wat",N}, val1 := [N,1,2,3,N]}} = lists:foldl(fun
+ ({V2,V3},{Old2,Old3,Mi}) ->
+ ok = check_val(Mi,Old2,Old3),
+ #{ val1 := Old2, val2 := Old3 } = Mi,
+ {V2,V3, Mi#{ val1 := id(V2), val2 := V1, val2 => id(V3)}}
+ end, {none, none, #{val1=>none,val2=>none}},List),
+ ok.
+
+get_val(#{ "wazzup" := _, val := V}) -> V;
+get_val(#{ val := V }) -> {some_val, V}.
+
+check_val(#{val1:=V1, val2:=V2},V1,V2) -> ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_warn_pair_key_overloaded.erl b/lib/hipe/test/maps_SUITE_data/maps_warn_pair_key_overloaded.erl
new file mode 100644
index 0000000000..76b2a91f94
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_warn_pair_key_overloaded.erl
@@ -0,0 +1,27 @@
+-module(maps_warn_pair_key_overloaded).
+-export([test/0]).
+
+test() ->
+ #{ "hi1" := 42 } = id(#{ "hi1" => 1, "hi1" => 42 }),
+
+ #{ "hi1" := 1337, "hi2" := [2], "hi3" := 3 } = id(#{
+ "hi1" => erlang:atom_to_binary(?MODULE,utf8),
+ "hi1" => erlang:binary_to_atom(<<"wazzup">>,utf8),
+ "hi1" => erlang:binary_to_float(<<"3.1416">>),
+ "hi1" => erlang:float_to_binary(3.1416),
+ "hi2" => erlang:pid_to_list(self()),
+ "hi3" => erlang:float_to_binary(3.1416),
+ "hi2" => lists:subtract([1,2],[1]),
+ "hi3" => +3,
+ "hi1" => erlang:min(1,2),
+ "hi1" => erlang:hash({1,2},35),
+ "hi1" => erlang:phash({1,2},33),
+ "hi1" => erlang:phash2({1,2},34),
+ "hi1" => erlang:integer_to_binary(1337),
+ "hi1" => erlang:binary_to_integer(<<"1337">>),
+ "hi4" => erlang:float_to_binary(3.1416)
+ }),
+ ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/test/maps_SUITE_data/maps_warn_useless_build.erl b/lib/hipe/test/maps_SUITE_data/maps_warn_useless_build.erl
new file mode 100644
index 0000000000..6cb0366314
--- /dev/null
+++ b/lib/hipe/test/maps_SUITE_data/maps_warn_useless_build.erl
@@ -0,0 +1,9 @@
+-module(maps_warn_useless_build).
+-export([test/0]).
+
+test() ->
+ [#{ a => id(I)} || I <- [1,2,3]],
+ ok.
+
+%% Use this function to avoid compile-time evaluation of an expression.
+id(I) -> I.
diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index ed4b4dc8d2..fb7e4b91a0 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.10.2.2
+HIPE_VSN = 3.10.3
diff --git a/lib/hipe/x86/hipe_x86_assemble.erl b/lib/hipe/x86/hipe_x86_assemble.erl
index 7878c7219d..3f756769c4 100644
--- a/lib/hipe/x86/hipe_x86_assemble.erl
+++ b/lib/hipe/x86/hipe_x86_assemble.erl
@@ -21,7 +21,6 @@
%%%
%%% TODO:
%%% - Simplify combine_label_maps and mk_data_relocs.
-%%% - Move find_const to hipe_pack_constants?
-ifdef(HIPE_AMD64).
-define(HIPE_X86_ASSEMBLE, hipe_amd64_assemble).
@@ -80,8 +79,8 @@ assemble(CompiledCode, Closures, Exports, Options) ->
%% ?debug_msg("Constants are ~w bytes\n",[ConstSize])),
%%
SC = hipe_pack_constants:slim_constmap(ConstMap),
- DataRelocs = mk_data_relocs(RefsFromConsts, LabelMap),
- SSE = slim_sorted_exportmap(ExportMap,Closures,Exports),
+ DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
+ SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap,Closures,Exports),
SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
Bin = term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC},
ConstAlign, ConstSize,
@@ -442,7 +441,7 @@ translate_imm(#x86_imm{value=Imm}, Context, MayTrunc8) ->
case Imm of
{Label,constant} ->
{MFA,ConstMap} = Context,
- ConstNo = find_const({MFA,Label}, ConstMap),
+ ConstNo = hipe_pack_constants:find_const({MFA,Label}, ConstMap),
{constant,ConstNo};
{Label,closure} ->
{closure,Label};
@@ -712,7 +711,7 @@ resolve_jmp_switch_arg(I, _Context) ->
{rm64,hipe_amd64_encode:rm_mem(EA)}.
-else.
resolve_jmp_switch_arg(I, {MFA,ConstMap}) ->
- ConstNo = find_const({MFA,hipe_x86:jmp_switch_jtab(I)}, ConstMap),
+ ConstNo = hipe_pack_constants:find_const({MFA,hipe_x86:jmp_switch_jtab(I)}, ConstMap),
Disp32 = {?LOAD_ADDRESS,{constant,ConstNo}},
SINDEX = ?HIPE_X86_ENCODE:sindex(2, hipe_x86:temp_reg(hipe_x86:jmp_switch_temp(I))),
EA = ?HIPE_X86_ENCODE:ea_disp32_sindex(Disp32, SINDEX), % this creates a SIB implicitly
@@ -932,37 +931,6 @@ resolve_x87_binop_args(Src=#x86_fpreg{}, Dst=#x86_fpreg{})->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-mk_data_relocs(RefsFromConsts, LabelMap) ->
- lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])).
-
-mk_data_relocs([{MFA,Labels} | Rest], LabelMap, Acc) ->
- Map = [case Label of
- {L,Pos} ->
- Offset = find({MFA,L}, LabelMap),
- {Pos,Offset};
- {sorted,Base,OrderedLabels} ->
- {sorted, Base, [begin
- Offset = find({MFA,L}, LabelMap),
- {Order, Offset}
- end
- || {L,Order} <- OrderedLabels]}
- end
- || Label <- Labels],
- %% msg("Map: ~w Map\n",[Map]),
- mk_data_relocs(Rest, LabelMap, [Map,Acc]);
-mk_data_relocs([],_,Acc) -> Acc.
-
-find({MFA,L},LabelMap) ->
- gb_trees:get({MFA,L}, LabelMap).
-
-slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) ->
- IsClosure = lists:member({M,F,A}, Closures),
- IsExported = is_exported(F, A, Exports),
- [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)];
-slim_sorted_exportmap([],_,_) -> [].
-
-is_exported(F, A, Exports) -> lists:member({F,A}, Exports).
-
%%%
%%% Assembly listing support (pp_asm option).
%%%
@@ -1001,14 +969,3 @@ fill_spaces(N) when N > 0 ->
fill_spaces(N-1);
fill_spaces(0) ->
[].
-
-%%%
-%%% Lookup a constant in a ConstMap.
-%%%
-
-find_const({MFA,Label},[{pcm_entry,MFA,Label,ConstNo,_,_,_}|_]) ->
- ConstNo;
-find_const(N,[_|R]) ->
- find_const(N,R);
-find_const(C,[]) ->
- ?EXIT({constant_not_found,C}).
diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml
index 9ae464e028..03af316f75 100644
--- a/lib/ic/doc/src/notes.xml
+++ b/lib/ic/doc/src/notes.xml
@@ -30,7 +30,23 @@
<file>notes.xml</file>
</header>
- <section><title>IC 4.3.4</title>
+ <section><title>IC 4.3.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Added Latin-1 code directive in the generated files
+ to keep old behaviour. Updated IC so it can handle
+ Unicode characters in the path. </p>
+ <p>
+ Own Id: OTP-11783</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>IC 4.3.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ic/src/ic.app.src b/lib/ic/src/ic.app.src
index 29aa6def00..7dd47ac9c6 100644
--- a/lib/ic/src/ic.app.src
+++ b/lib/ic/src/ic.app.src
@@ -46,7 +46,8 @@
{registered, []},
{applications, [stdlib, kernel]},
{env, []},
- {mod, {ic, []}}
+ {mod, {ic, []}},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/ic/src/ic.erl b/lib/ic/src/ic.erl
index c0742cf7bd..4f3e8d180c 100644
--- a/lib/ic/src/ic.erl
+++ b/lib/ic/src/ic.erl
@@ -250,7 +250,7 @@ make_erl_options(Opts) ->
Optimize = Opts#options.optimize,
PreProc =
lists:flatten(
- lists:map(fun(D) -> io_lib:format("-I\"~s\" ", [ic_util:to_list(D)]) end,
+ lists:map(fun(D) -> io_lib:format("-I\"~ts\" ", [ic_util:to_list(D)]) end,
Includes1)++
lists:map(
fun ({Name, Value}) ->
diff --git a/lib/ic/src/ic_codegen.erl b/lib/ic/src/ic_codegen.erl
index f611c69bea..82431b79a2 100644
--- a/lib/ic/src/ic_codegen.erl
+++ b/lib/ic/src/ic_codegen.erl
@@ -216,12 +216,14 @@ exp_to_string({F,N}) -> io_lib:format("~p/~p", [ic_util:to_atom(F), N]).
%%--------------------------------------------------------------------
emit_stub_head(_G, ignore, _Name, _) -> ignore;
emit_stub_head(G, F1, Name, erlang) ->
+ comment(F1, " coding: latin-1", []),
mcomment(F1, stub_header(G, Name)),
nl(F1),
emit(F1, "-module(~p).\n", [list_to_atom(Name)]),
emit(F1, "-ic_compiled(~p).\n", [compiler_vsn(?COMPILERVSN)]),
emit(F1, "\n\n"), F1;
emit_stub_head(G, F1, Name, erlang_template) ->
+ comment(F1, " coding: latin-1", []),
ic_erl_template:emit_header(G, F1, Name),
F1;
emit_stub_head(_G, F1, _Name, erlang_template_no_gen) ->
@@ -259,6 +261,7 @@ compiler_vsn(Vsn) ->
%% Name is Fully scoped (undescore) name of interface or module
emit_hrl_head(_G, ignore, _Name, _) -> ignore;
emit_hrl_head(G, Fd, Name, erlang) ->
+ comment(Fd, " coding: latin-1", []),
mcomment(Fd, ["Erlang header file" |
hrl_header(G, Name)]),
nl(Fd),
diff --git a/lib/ic/src/ic_pp.erl b/lib/ic/src/ic_pp.erl
index 54701f7438..e54304ebaa 100644
--- a/lib/ic/src/ic_pp.erl
+++ b/lib/ic/src/ic_pp.erl
@@ -257,15 +257,15 @@ run_include(FileName, FileList, _Out, Defs, Err, War, IncLine, IncFile, IncDir,
tokenise(File, FileName) ->
{Result, _L} = token(File, 2, [], not_set, 0),
- FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 ~p~n",[FileName]))),
+ FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 \"~ts\"~n",[FileName]))),
FileInfoStart = {file_info, FI_start},
[FileInfoStart | Result].
tokenise(File, FileName, IncLine, PrevFile) ->
{Result, _L} = token(File, 2, [], not_set, 0),
- FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 ~p 1~n",[FileName]))),
+ FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 \"~ts\" 1~n",[FileName]))),
FileInfoStart = {file_info, FI_start},
- FI_end = lists:reverse(lists:flatten(io_lib:format("# ~p ~p 2~n~n",[IncLine-1,PrevFile]))),
+ FI_end = lists:reverse(lists:flatten(io_lib:format("# ~p \"~ts\" 2~n~n",[IncLine-1,PrevFile]))),
FileInfoEnd = [{file_info, FI_end}],
{Result, FileInfoStart, FileInfoEnd}.
% [FileInfoStart | Result] ++ FileInfoEnd.
@@ -1942,7 +1942,7 @@ read_inc_file(FileName, IncDir, Mio) ->
FileList = binary_to_list(Bin),
{ok, AbsFile, FileList};
{error, Text} ->
- {error, Text}
+ {error, Text}
end;
true ->
skip
diff --git a/lib/ic/test/java_client_erl_server_SUITE.erl b/lib/ic/test/java_client_erl_server_SUITE.erl
index 9e49305c3c..cbcf32515e 100644
--- a/lib/ic/test/java_client_erl_server_SUITE.erl
+++ b/lib/ic/test/java_client_erl_server_SUITE.erl
@@ -288,7 +288,7 @@ classpath(Dir) ->
cmd(Cmd) ->
PortOpts = [{line,80},eof,exit_status,stderr_to_stdout],
- io:format("<cmd> ~s~n", [Cmd]),
+ io:format("<cmd> ~ts~n", [Cmd]),
case catch open_port({spawn,Cmd}, PortOpts) of
Port when is_port(Port) ->
Result = cmd_loop(Port, []),
diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk
index fe27d095d3..2ffbbad444 100644
--- a/lib/ic/vsn.mk
+++ b/lib/ic/vsn.mk
@@ -1 +1 @@
-IC_VSN = 4.3.4
+IC_VSN = 4.3.5
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index f77214c589..596c0d77f4 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>2013</year>
+ <year>2002</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,7 +32,128 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 5.9.8</title>
+ <section><title>Inets 5.10.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Correct distirbing mode for httpd:reload_config/2</p>
+ <p>
+ Own Id: OTP-11914</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved handling of invalid strings in the HTTP request
+ line.</p>
+ <p>
+ Impact: May improve memory consumption</p>
+ <p>
+ Own Id: OTP-11925 Aux Id: Sequence 12601 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a spelling mistake in httpc doc (Thanks to Wasif
+ Riaz Malik)</p>
+ <p>
+ Own Id: OTP-11538</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ <item>
+ <p>
+ ftp now sanitize file name, user name and passwords from
+ &lt;CR&gt; and &lt;LF&gt; tags (Thanks to Sergei Golovan)</p>
+ <p>
+ Own Id: OTP-11750</p>
+ </item>
+ <item>
+ <p>
+ Corrected error handling in the HTTP client, making it
+ behave more graceful.</p>
+ <p>
+ Thanks to Kirilll Zaborsky</p>
+ <p>
+ Own Id: OTP-11794</p>
+ </item>
+ <item>
+ <p>
+ Support identity transfer-encoding in httpc.</p>
+ <p>
+ Thanks to Anthony Ramine</p>
+ <p>
+ Own Id: OTP-11802</p>
+ </item>
+ <item>
+ <p>
+ Ignore empty Set-Cookie headers to increase
+ interoperability with servers that violate the RFC. </p>
+ <p>
+ Thanks to Kirilll Zaborsky</p>
+ <p>
+ Own Id: OTP-11803</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The commit 6189bc07 "inets: httpc improve pipelining" has
+ been reverted, as it turned out to break things rather
+ than improve pipelining utilization. It is instead up to
+ the user to configure httpc and use it wisely to be able
+ to get the most out of pipelining.</p>
+ <p>
+ Own Id: OTP-11756</p>
+ </item>
+ <item>
+ <p>
+ Handle all response codes in httpd_util:message/3</p>
+ <p>
+ Own Id: OTP-11838</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.9.8</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/inets/src/http_client/httpc_cookie.erl b/lib/inets/src/http_client/httpc_cookie.erl
index 69900bae65..134115bdfa 100644
--- a/lib/inets/src/http_client/httpc_cookie.erl
+++ b/lib/inets/src/http_client/httpc_cookie.erl
@@ -335,7 +335,8 @@ add_domain(Str, #http_cookie{domain = Domain}) ->
Str ++ "; $Domain=" ++ Domain.
parse_set_cookies(CookieHeaders, DefaultPathDomain) ->
- SetCookieHeaders = [Value || {"set-cookie", Value} <- CookieHeaders],
+ %% empty Set-Cookie header is invalid according to RFC but some sites violate it
+ SetCookieHeaders = [Value || {"set-cookie", Value} <- CookieHeaders, Value /= ""],
Cookies = [parse_set_cookie(SetCookieHeader, DefaultPathDomain) ||
SetCookieHeader <- SetCookieHeaders],
%% print_cookies("Parsed Cookies", Cookies),
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index a89a457a51..5ae6760f08 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1116,18 +1116,19 @@ handle_http_body(Body, #state{headers = Headers,
{new_body, NewBody}]),
NewHeaders = http_chunk:handle_headers(Headers,
ChunkedHeaders),
- handle_response(State#state{headers = NewHeaders,
- body = NewBody})
+ case Body of
+ <<>> ->
+ handle_response(State#state{headers = NewHeaders,
+ body = NewBody});
+ _ ->
+ {NewBody2, NewRequest} =
+ stream(NewBody, Request, Code),
+ handle_response(State#state{headers = NewHeaders,
+ body = NewBody2})
+ end
end;
- Encoding when is_list(Encoding) ->
- ?hcrt("handle_http_body - encoding", [{encoding, Encoding}]),
- NewState = answer_request(Request,
- httpc_response:error(Request,
- unknown_encoding),
- State),
- {stop, normal, NewState};
- _ ->
- ?hcrt("handle_http_body - other", []),
+ Enc when Enc =:= "identity"; Enc =:= undefined ->
+ ?hcrt("handle_http_body - identity", []),
Length =
list_to_integer(Headers#http_response_h.'content-length'),
case ((Length =< MaxBodySize) orelse (MaxBodySize =:= nolimit)) of
@@ -1149,12 +1150,19 @@ handle_http_body(Body, #state{headers = Headers,
body_too_big),
State),
{stop, normal, NewState}
- end
+ end;
+ Encoding when is_list(Encoding) ->
+ ?hcrt("handle_http_body - other", [{encoding, Encoding}]),
+ NewState = answer_request(Request,
+ httpc_response:error(Request,
+ unknown_encoding),
+ State),
+ {stop, normal, NewState}
end.
handle_response(#state{status = new} = State) ->
?hcrd("handle response - status = new", []),
- handle_response(check_persistent(State));
+ handle_response(try_to_enable_pipeline_or_keep_alive(State));
handle_response(#state{request = Request,
status = Status,
@@ -1218,6 +1226,7 @@ handle_response(#state{request = Request,
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);
@@ -1429,22 +1438,39 @@ is_keep_alive_enabled_server(_,_) ->
is_keep_alive_connection(Headers, #session{client_close = ClientClose}) ->
(not ((ClientClose) orelse httpc_response:is_server_closing(Headers))).
-check_persistent(
- #state{session = #session{type = Type} = Session,
+try_to_enable_pipeline_or_keep_alive(
+ #state{session = Session,
+ request = #request{method = Method},
status_line = {Version, _, _},
headers = Headers,
- profile_name = ProfileName} = State) ->
+ profile_name = ProfileName} = State) ->
+ ?hcrd("try to enable pipeline or keep-alive",
+ [{version, Version},
+ {headers, Headers},
+ {session, Session}]),
case is_keep_alive_enabled_server(Version, Headers) andalso
- is_keep_alive_connection(Headers, Session) of
+ is_keep_alive_connection(Headers, Session) of
true ->
- mark_persistent(ProfileName, Session),
- State#state{status = Type};
+ case (is_pipeline_enabled_client(Session) andalso
+ httpc_request:is_idempotent(Method)) of
+ true ->
+ insert_session(Session, ProfileName),
+ State#state{status = pipeline};
+ false ->
+ insert_session(Session, ProfileName),
+ %% Make sure type is keep_alive in session
+ %% as it in this case might be pipeline
+ NewSession = Session#session{type = keep_alive},
+ State#state{status = keep_alive,
+ session = NewSession}
+ end;
false ->
State#state{status = close}
end.
answer_request(#request{id = RequestId, from = From} = Request, Msg,
- #state{timers = Timers,
+ #state{session = Session,
+ timers = Timers,
profile_name = ProfileName} = State) ->
?hcrt("answer request", [{request, Request}, {msg, Msg}]),
httpc_response:send(From, Msg),
@@ -1454,14 +1480,19 @@ answer_request(#request{id = RequestId, from = From} = Request, Msg,
Timer = {RequestId, TimerRef},
cancel_timer(TimerRef, {timeout, Request#request.id}),
httpc_manager:request_done(RequestId, ProfileName),
+ NewSession = maybe_make_session_available(ProfileName, Session),
Timers2 = Timers#timers{request_timers = lists:delete(Timer,
RequestTimers)},
State#state{request = Request#request{from = answer_sent},
+ session = NewSession,
timers = Timers2}.
-mark_persistent(ProfileName, Session) ->
- update_session(ProfileName, Session, #session.persistent, true),
- Session#session{persistent = true}.
+maybe_make_session_available(ProfileName,
+ #session{available = false} = Session) ->
+ update_session(ProfileName, Session, #session.available, true),
+ Session#session{available = true};
+maybe_make_session_available(_ProfileName, Session) ->
+ Session.
cancel_timers(#timers{request_timers = ReqTmrs, queue_timer = QTmr}) ->
cancel_timer(QTmr, timeout_queue),
@@ -1626,6 +1657,10 @@ start_stream(_StatusLine, _Headers, Request) ->
?hcrt("start stream - no op", []),
{ok, Request}.
+stream_remaining_body(<<>>, _, _) ->
+ ok;
+stream_remaining_body(Body, Request, {_, Code, _}) ->
+ stream(Body, Request, Code).
%% Note the end stream message is handled by httpc_response and will
%% be sent by answer_request
@@ -1829,7 +1864,7 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
[ProfileName, SessionId, Pos, Value,
(catch httpc_manager:which_session_info(ProfileName)),
Session,
- (catch httpc_manager:lookup_session(ProfileName, SessionId)),
+ (catch httpc_manager:lookup_session(SessionId, ProfileName)),
T, E]),
exit({failed_updating_session,
[{profile, ProfileName},
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index d5b3dd2a2a..add5d11dfa 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -143,8 +143,8 @@
%% true | false
%% This will be true, when a response has been received for
- %% the first request and the server has not closed the connection
- persistent = false
+ %% the first request. See type above.
+ available = false
}).
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index a3ed371e61..48a9c32454 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -451,7 +451,7 @@ do_init(ProfileName, CookiesDir) ->
%%--------------------------------------------------------------------
handle_call({request, Request}, _, State) ->
?hcri("request", [{request, Request}]),
- case (catch handle_request(Request, State, false)) of
+ case (catch handle_request(Request, State)) of
{reply, Msg, NewState} ->
{reply, Msg, NewState};
Error ->
@@ -511,7 +511,7 @@ handle_cast({retry_or_redirect_request, {Time, Request}},
{noreply, State};
handle_cast({retry_or_redirect_request, Request}, State) ->
- case (catch handle_request(Request, State, true)) of
+ case (catch handle_request(Request, State)) of
{reply, {ok, _}, NewState} ->
{noreply, NewState};
Error ->
@@ -724,7 +724,7 @@ get_handler_info(Tab) ->
handle_request(#request{settings =
#http_options{version = "HTTP/0.9"}} = Request,
- State, _) ->
+ State) ->
%% Act as an HTTP/0.9 client that does not know anything
%% about persistent connections
@@ -737,7 +737,7 @@ handle_request(#request{settings =
handle_request(#request{settings =
#http_options{version = "HTTP/1.0"}} = Request,
- State, _) ->
+ State) ->
%% Act as an HTTP/1.0 client that does not
%% use persistent connections
@@ -748,13 +748,13 @@ handle_request(#request{settings =
start_handler(NewRequest#request{headers = NewHeaders}, State),
{reply, {ok, NewRequest#request.id}, State};
-handle_request(Request, State = #state{options = Options}, Retry) ->
+handle_request(Request, State = #state{options = Options}) ->
NewRequest = handle_cookies(generate_request_id(Request), State),
SessionType = session_type(Options),
case select_session(Request#request.method,
Request#request.address,
- Request#request.scheme, SessionType, State, Retry) of
+ Request#request.scheme, SessionType, State) of
{ok, HandlerPid} ->
pipeline_or_keep_alive(NewRequest, HandlerPid, State);
no_connection ->
@@ -778,7 +778,6 @@ start_handler(#request{id = Id,
#state{profile_name = ProfileName,
handler_db = HandlerDb,
options = Options}) ->
- ClientClose = httpc_request:is_client_closing(Request#request.headers),
{ok, Pid} =
case is_inets_manager() of
true ->
@@ -789,18 +788,13 @@ start_handler(#request{id = Id,
end,
HandlerInfo = {Id, Pid, From},
ets:insert(HandlerDb, HandlerInfo),
- insert_session(#session{id = {Request#request.address, Pid},
- scheme = Request#request.scheme,
- client_close = ClientClose,
- type = session_type(Options)
- }, ProfileName),
erlang:monitor(process, Pid).
select_session(Method, HostPort, Scheme, SessionType,
#state{options = #options{max_pipeline_length = MaxPipe,
max_keep_alive_length = MaxKeepAlive},
- session_db = SessionDb}, Retry) ->
+ session_db = SessionDb}) ->
?hcrd("select session", [{session_type, SessionType},
{max_pipeline_length, MaxPipe},
{max_keep_alive_length, MaxKeepAlive}]),
@@ -813,23 +807,13 @@ select_session(Method, HostPort, Scheme, SessionType,
%% client_close, scheme and type specified.
%% The fields id (part of: HandlerPid) and queue_length
%% specified.
- Pattern = case (Retry andalso SessionType == pipeline) of
- true ->
- #session{id = {HostPort, '$1'},
- client_close = false,
- scheme = Scheme,
- queue_length = '$2',
- type = SessionType,
- persistent = true,
- _ = '_'};
- false ->
- #session{id = {HostPort, '$1'},
- client_close = false,
- scheme = Scheme,
- queue_length = '$2',
- type = SessionType,
- _ = '_'}
- end,
+ Pattern = #session{id = {HostPort, '$1'},
+ client_close = false,
+ scheme = Scheme,
+ queue_length = '$2',
+ type = SessionType,
+ available = true,
+ _ = '_'},
%% {'_', {HostPort, '$1'}, false, Scheme, '_', '$2', SessionTyp},
Candidates = ets:match(SessionDb, Pattern),
?hcrd("select session", [{host_port, HostPort},
diff --git a/lib/inets/src/http_lib/http_internal.hrl b/lib/inets/src/http_lib/http_internal.hrl
index 97cf474ab9..53b776c4e7 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-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,8 @@
-define(HTTP_MAX_BODY_SIZE, nolimit).
-define(HTTP_MAX_HEADER_SIZE, 10240).
-define(HTTP_MAX_URI_SIZE, nolimit).
+-define(HTTP_MAX_VERSION_STRING, 8).
+-define(HTTP_MAX_METHOD_STRING, 20).
-ifndef(HTTP_DEFAULT_SSL_KIND).
-define(HTTP_DEFAULT_SSL_KIND, essl).
diff --git a/lib/inets/src/http_server/httpd.erl b/lib/inets/src/http_server/httpd.erl
index 6052ae9022..e8148ea362 100644
--- a/lib/inets/src/http_server/httpd.erl
+++ b/lib/inets/src/http_server/httpd.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -294,9 +294,13 @@ do_reload_config(ConfigList, Mode) ->
{ok, Config} ->
Address = proplists:get_value(bind_address, Config, any),
Port = proplists:get_value(port, Config, 80),
- block(Address, Port, Mode),
- reload(Config, Address, Port),
- unblock(Address, Port);
+ case block(Address, Port, Mode) of
+ ok ->
+ reload(Config, Address, Port),
+ unblock(Address, Port);
+ Error ->
+ Error
+ end;
Error ->
Error
end.
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 16a080f8e2..6fc07f033c 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -66,7 +66,7 @@ get_bin(_Env,_Input) ->
<INPUT TYPE=\"text\" NAME=\"input2\">
<INPUT TYPE=\"submit\"><BR>
</FORM>" ++ "\n"),
- footer()].
+ list_to_binary(footer())].
post(_Env,[]) ->
[header(),
diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl
index e155498bb8..3da0343401 100644
--- a/lib/inets/src/http_server/httpd_manager.erl
+++ b/lib/inets/src/http_server/httpd_manager.erl
@@ -210,9 +210,10 @@ handle_call({block , Blocker, Mode, Timeout}, From,
handle_call({block , _, _, _}, _, State) ->
{reply, {error, blocked}, State};
-handle_call({unblock, Blocker}, _, #state{blocker_ref = {Blocker,_},
+handle_call({unblock, Blocker}, _, #state{blocker_ref = {Blocker, Monitor},
admin_state = blocked} = State) ->
-
+
+ erlang:demonitor(Monitor),
{reply, ok,
State#state{admin_state = unblocked, blocker_ref = undefined}};
@@ -247,37 +248,36 @@ handle_cast(Message, State) ->
handle_info(connections_terminated, #state{admin_state = shutting_down,
blocking_from = From} = State) ->
gen_server:reply(From, ok),
- {noreply, State#state{admin_state = blocked, blocking_from = undefined,
- blocker_ref = undefined}};
+ {noreply, State#state{admin_state = blocked, blocking_from = undefined}};
handle_info(connections_terminated, State) ->
{noreply, State};
-handle_info({block_timeout, non_disturbing},
+handle_info({block_timeout, non_disturbing, Blocker},
#state{admin_state = shutting_down,
blocking_from = From,
- blocker_ref = {_, Monitor}} = State) ->
+ blocker_ref = {_, Monitor} = Blocker} = State) ->
erlang:demonitor(Monitor),
gen_server:reply(From, {error, timeout}),
{noreply, State#state{admin_state = unblocked, blocking_from = undefined,
blocker_ref = undefined}};
-handle_info({block_timeout, disturbing},
+handle_info({block_timeout, disturbing, Blocker},
#state{admin_state = shutting_down,
blocking_from = From,
- blocker_ref = {_, Monitor},
+ blocker_ref = Blocker,
connection_sup = Sup} = State) ->
SupPid = whereis(Sup),
shutdown_connections(SupPid),
- erlang:demonitor(Monitor),
gen_server:reply(From, ok),
- {noreply, State#state{admin_state = blocked, blocker_ref = undefined,
+ {noreply, State#state{admin_state = blocked,
blocking_from = undefined}};
handle_info({block_timeout, _, _}, State) ->
{noreply, State};
handle_info({'DOWN', _, process, Pid, _Info},
#state{admin_state = Admin,
- blocker_ref = {Pid, _}} = State) when
+ blocker_ref = {Pid, Monitor}} = State) when
Admin =/= unblocked ->
+ erlang:demonitor(Monitor),
{noreply, State#state{admin_state = unblocked,
blocking_from = undefined,
blocker_ref = undefined}};
@@ -333,18 +333,16 @@ handle_new_connection(_UsageState, _AdminState, State, _Handler) ->
handle_block(disturbing, infinity,
#state{connection_sup = CSup,
- blocking_from = From,
- blocker_ref = {_, Monitor}} = State) ->
+ blocking_from = From} = State) ->
SupPid = whereis(CSup),
shutdown_connections(SupPid),
- erlang:demonitor(Monitor),
gen_server:reply(From, ok),
- {noreply, State#state{admin_state = blocked, blocker_ref = undefined,
+ {noreply, State#state{admin_state = blocked,
blocking_from = undefined}};
-handle_block(disturbing, Timeout, #state{connection_sup = CSup} = State) ->
+handle_block(disturbing, Timeout, #state{connection_sup = CSup, blocker_ref = Blocker} = State) ->
Manager = self(),
spawn_link(fun() -> wait_for_shutdown(CSup, Manager) end),
- erlang:send_after(Timeout, self(), {block_timeout, disturbing}),
+ erlang:send_after(Timeout, self(), {block_timeout, disturbing, Blocker}),
{noreply, State#state{admin_state = shutting_down}};
handle_block(non_disturbing, infinity,
@@ -354,10 +352,10 @@ handle_block(non_disturbing, infinity,
{noreply, State#state{admin_state = shutting_down}};
handle_block(non_disturbing, Timeout,
- #state{connection_sup = CSup} = State) ->
+ #state{connection_sup = CSup, blocker_ref = Blocker} = State) ->
Manager = self(),
spawn_link(fun() -> wait_for_shutdown(CSup, Manager) end),
- erlang:send_after(Timeout, self(), {block_timeout, non_disturbing}),
+ erlang:send_after(Timeout, self(), {block_timeout, non_disturbing, Blocker}),
{noreply, State#state{admin_state = shutting_down}}.
handle_reload(undefined, #state{config_file = undefined} = State) ->
diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl
index 5ba79b2706..712c73599f 100644
--- a/lib/inets/src/http_server/httpd_request.erl
+++ b/lib/inets/src/http_server/httpd_request.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -44,26 +44,26 @@
%%%=========================================================================
parse([Bin, MaxSizes]) ->
?hdrt("parse", [{bin, Bin}, {max_sizes, MaxSizes}]),
- parse_method(Bin, [], MaxSizes, []);
+ parse_method(Bin, [], 0, proplists:get_value(max_method, MaxSizes), MaxSizes, []);
parse(Unknown) ->
?hdrt("parse", [{unknown, Unknown}]),
exit({bad_args, Unknown}).
%% Functions that may be returned during the decoding process
%% if the input data is incompleate.
-parse_method([Bin, Method, MaxSizes, Result]) ->
- parse_method(Bin, Method, MaxSizes, Result).
+parse_method([Bin, Method, Current, Max, MaxSizes, Result]) ->
+ parse_method(Bin, Method, Current, Max, MaxSizes, Result).
-parse_uri([Bin, URI, CurrSize, MaxSizes, Result]) ->
- parse_uri(Bin, URI, CurrSize, MaxSizes, Result).
+parse_uri([Bin, URI, Current, Max, MaxSizes, Result]) ->
+ parse_uri(Bin, URI, Current, Max, MaxSizes, Result).
-parse_version([Bin, Rest, Version, MaxSizes, Result]) ->
- parse_version(<<Rest/binary, Bin/binary>>, Version, MaxSizes,
+parse_version([Bin, Rest, Version, Current, Max, MaxSizes, Result]) ->
+ parse_version(<<Rest/binary, Bin/binary>>, Version, Current, Max, MaxSizes,
Result).
-parse_headers([Bin, Rest, Header, Headers, CurrSize, MaxSizes, Result]) ->
+parse_headers([Bin, Rest, Header, Headers, Current, Max, MaxSizes, Result]) ->
parse_headers(<<Rest/binary, Bin/binary>>,
- Header, Headers, CurrSize, MaxSizes, Result).
+ Header, Headers, Current, Max, MaxSizes, Result).
whole_body([Bin, Body, Length]) ->
whole_body(<<Body/binary, Bin/binary>>, Length).
@@ -107,8 +107,12 @@ validate("POST", Uri, "HTTP/1." ++ _N) ->
validate("TRACE", Uri, "HTTP/1." ++ N) when hd(N) >= $1 ->
validate_uri(Uri);
validate(Method, Uri, Version) ->
- {error, {not_supported, {Method, Uri, Version}}}.
-
+ case validate_version(Version) of
+ true ->
+ {error, {not_supported, {Method, Uri, Version}}};
+ false ->
+ {error, {bad_version, Version}}
+ end.
%%----------------------------------------------------------------------
%% The request is passed through the server as a record of type mod
%% create it.
@@ -131,104 +135,75 @@ update_mod_data(ModData, Method, RequestURI, HTTPVersion, Headers)->
%%%========================================================================
%%% Internal functions
%%%========================================================================
-parse_method(<<>>, Method, MaxSizes, Result) ->
- ?hdrt("parse_method - empty bin",
- [{method, Method}, {max_sizes, MaxSizes}, {result, Result}]),
- {?MODULE, parse_method, [Method, MaxSizes, Result]};
-parse_method(<<?SP, Rest/binary>>, Method, MaxSizes, Result) ->
- ?hdrt("parse_method - SP begin",
- [{rest, Rest},
- {method, Method},
- {max_sizes, MaxSizes},
- {result, Result}]),
- parse_uri(Rest, [], 0, MaxSizes,
+parse_method(<<>>, Method, Current, Max, MaxSizes, Result) ->
+ {?MODULE, parse_method, [Method, Current, Max, MaxSizes, Result]};
+parse_method(<<?SP, Rest/binary>>, Method, _Current, _Max, MaxSizes, Result) ->
+ parse_uri(Rest, [], 0, proplists:get_value(max_uri, MaxSizes), MaxSizes,
[string:strip(lists:reverse(Method)) | Result]);
-parse_method(<<Octet, Rest/binary>>, Method, MaxSizes, Result) ->
- ?hdrt("parse_method",
- [{octet, Octet},
- {rest, Rest},
- {method, Method},
- {max_sizes, MaxSizes},
- {result, Result}]),
- parse_method(Rest, [Octet | Method], MaxSizes, Result).
-
-parse_uri(_, _, CurrSize, {MaxURI, _}, _)
- when (CurrSize > MaxURI) andalso (MaxURI =/= nolimit) ->
- ?hdrt("parse_uri",
- [{current_size, CurrSize},
- {max_uri, MaxURI}]),
+parse_method(<<Octet, Rest/binary>>, Method, Current, Max, MaxSizes, Result) when Current =< Max ->
+ parse_method(Rest, [Octet | Method], Current + 1, Max, MaxSizes, Result);
+parse_method(_, _, _, Max, _, _) ->
+ %% We do not know the version of the client as it comes after the
+ %% method send the lowest version in the response so that the client
+ %% will be able to handle it.
+ {error, {too_long, Max, 413, "Method unreasonably long"}, lowest_version()}.
+
+parse_uri(_, _, Current, MaxURI, _, _)
+ when (Current > MaxURI) andalso (MaxURI =/= nolimit) ->
%% We do not know the version of the client as it comes after the
%% uri send the lowest version in the response so that the client
%% will be able to handle it.
- HttpVersion = "HTTP/0.9",
- {error, {uri_too_long, MaxURI}, HttpVersion};
-parse_uri(<<>>, URI, CurrSize, MaxSizes, Result) ->
- ?hdrt("parse_uri - empty bin",
- [{uri, URI},
- {current_size, CurrSize},
- {max_sz, MaxSizes},
- {result, Result}]),
- {?MODULE, parse_uri, [URI, CurrSize, MaxSizes, Result]};
-parse_uri(<<?SP, Rest/binary>>, URI, _, MaxSizes, Result) ->
- ?hdrt("parse_uri - SP begin",
- [{uri, URI},
- {max_sz, MaxSizes},
- {result, Result}]),
- parse_version(Rest, [], MaxSizes,
+ {error, {too_long, MaxURI, 414, "URI unreasonably long"},lowest_version()};
+parse_uri(<<>>, URI, Current, Max, MaxSizes, Result) ->
+ {?MODULE, parse_uri, [URI, Current, Max, MaxSizes, Result]};
+parse_uri(<<?SP, Rest/binary>>, URI, _, _, MaxSizes, Result) ->
+ parse_version(Rest, [], 0, proplists:get_value(max_version, MaxSizes), MaxSizes,
[string:strip(lists:reverse(URI)) | Result]);
%% Can happen if it is a simple HTTP/0.9 request e.i "GET /\r\n\r\n"
-parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, MaxSizes, Result) ->
- ?hdrt("parse_uri - CR begin",
- [{uri, URI},
- {max_sz, MaxSizes},
- {result, Result}]),
- parse_version(Data, [], MaxSizes,
+parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, _, MaxSizes, Result) ->
+ parse_version(Data, [], 0, proplists:get_value(max_version, MaxSizes), MaxSizes,
[string:strip(lists:reverse(URI)) | Result]);
-parse_uri(<<Octet, Rest/binary>>, URI, CurrSize, MaxSizes, Result) ->
- ?hdrt("parse_uri",
- [{octet, Octet},
- {uri, URI},
- {curr_sz, CurrSize},
- {max_sz, MaxSizes},
- {result, Result}]),
- parse_uri(Rest, [Octet | URI], CurrSize + 1, MaxSizes, Result).
-
-parse_version(<<>>, Version, MaxSizes, Result) ->
- {?MODULE, parse_version, [<<>>, Version, MaxSizes, Result]};
-parse_version(<<?LF, Rest/binary>>, Version, MaxSizes, Result) ->
+parse_uri(<<Octet, Rest/binary>>, URI, Current, Max, MaxSizes, Result) ->
+ parse_uri(Rest, [Octet | URI], Current + 1, Max, MaxSizes, Result).
+
+parse_version(<<>>, Version, Current, Max, MaxSizes, Result) ->
+ {?MODULE, parse_version, [<<>>, Version, Current, Max, MaxSizes, Result]};
+parse_version(<<?LF, Rest/binary>>, Version, Current, Max, MaxSizes, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_version(<<?CR, ?LF, Rest/binary>>, Version, MaxSizes, Result);
-parse_version(<<?CR, ?LF, Rest/binary>>, Version, MaxSizes, Result) ->
- parse_headers(Rest, [], [], 0, MaxSizes,
+ parse_version(<<?CR, ?LF, Rest/binary>>, Version, Current, Max, MaxSizes, Result);
+parse_version(<<?CR, ?LF, Rest/binary>>, Version, _, _, MaxSizes, Result) ->
+ parse_headers(Rest, [], [], 0, proplists:get_value(max_header, MaxSizes), MaxSizes,
[string:strip(lists:reverse(Version)) | Result]);
-parse_version(<<?CR>> = Data, Version, MaxSizes, Result) ->
- {?MODULE, parse_version, [Data, Version, MaxSizes, Result]};
-parse_version(<<Octet, Rest/binary>>, Version, MaxSizes, Result) ->
- parse_version(Rest, [Octet | Version], MaxSizes, Result).
-
-parse_headers(_, _, _, CurrSize, {_, MaxHeaderSize}, Result)
- when CurrSize > MaxHeaderSize, MaxHeaderSize =/= nolimit ->
+parse_version(<<?CR>> = Data, Version, Current, Max, MaxSizes, Result) ->
+ {?MODULE, parse_version, [Data, Version, Current, Max, MaxSizes, Result]};
+parse_version(<<Octet, Rest/binary>>, Version, Current, Max, MaxSizes, Result) when Current =< Max ->
+ parse_version(Rest, [Octet | Version], Current + 1, Max, MaxSizes, Result);
+parse_version(_, _, _, Max,_,_) ->
+ {error, {too_long, Max, 413, "Version string unreasonably long"}, lowest_version()}.
+
+parse_headers(_, _, _, Current, Max, _, Result)
+ when Max =/= nolimit andalso Current > Max ->
HttpVersion = lists:nth(3, lists:reverse(Result)),
- {error, {header_too_long, MaxHeaderSize}, HttpVersion};
+ {error, {too_long, Max, 413, "Headers unreasonably long"}, HttpVersion};
-parse_headers(<<>>, Header, Headers, CurrSize, MaxSizes, Result) ->
- {?MODULE, parse_headers, [<<>>, Header, Headers, CurrSize,
+parse_headers(<<>>, Header, Headers, Current, Max, MaxSizes, Result) ->
+ {?MODULE, parse_headers, [<<>>, Header, Headers, Current, Max,
MaxSizes, Result]};
-parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], CurrSize, MaxSizes, Result) ->
+parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, MaxSizes, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], CurrSize,
+ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max,
MaxSizes, Result);
-parse_headers(<<?LF,?LF,Body/binary>>, [], [], CurrSize, MaxSizes, Result) ->
+parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, MaxSizes, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], CurrSize,
+ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max,
MaxSizes, Result);
-parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], _, _, Result) ->
+parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], _, _, _, Result) ->
NewResult = list_to_tuple(lists:reverse([Body, {#http_request_h{}, []} |
Result])),
{ok, NewResult};
-parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _,
+parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _, _,
_, Result) ->
HTTPHeaders = [lists:reverse(Header) | Headers],
RequestHeaderRcord =
@@ -238,52 +213,51 @@ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _,
HTTPHeaders} | Result])),
{ok, NewResult};
-parse_headers(<<?CR,?LF,?CR>> = Data, Header, Headers, CurrSize,
+parse_headers(<<?CR,?LF,?CR>> = Data, Header, Headers, Current, Max,
MaxSizes, Result) ->
- {?MODULE, parse_headers, [Data, Header, Headers, CurrSize,
+ {?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
MaxSizes, Result]};
-parse_headers(<<?LF>>, [], [], CurrSize, MaxSizes, Result) ->
+parse_headers(<<?LF>>, [], [], Current, Max, MaxSizes, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF>>, [], [], CurrSize, MaxSizes, Result);
+ parse_headers(<<?CR,?LF>>, [], [], Current, Max, MaxSizes, Result);
%% There where no headers, which is unlikely to happen.
-parse_headers(<<?CR,?LF>>, [], [], _, _, Result) ->
+parse_headers(<<?CR,?LF>>, [], [], _, _, _, Result) ->
NewResult = list_to_tuple(lists:reverse([<<>>, {#http_request_h{}, []} |
Result])),
{ok, NewResult};
-parse_headers(<<?LF>>, Header, Headers, CurrSize,
+parse_headers(<<?LF>>, Header, Headers, Current, Max,
MaxSizes, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF>>, Header, Headers, CurrSize, MaxSizes, Result);
+ parse_headers(<<?CR,?LF>>, Header, Headers, Current, Max, MaxSizes, Result);
-parse_headers(<<?CR,?LF>> = Data, Header, Headers, CurrSize,
+parse_headers(<<?CR,?LF>> = Data, Header, Headers, Current, Max,
MaxSizes, Result) ->
- {?MODULE, parse_headers, [Data, Header, Headers, CurrSize,
+ {?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
MaxSizes, Result]};
-parse_headers(<<?LF, Octet, Rest/binary>>, Header, Headers, CurrSize,
+parse_headers(<<?LF, Octet, Rest/binary>>, Header, Headers, Current, Max,
MaxSizes, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, CurrSize,
+ parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, Current, Max,
MaxSizes, Result);
-parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, CurrSize,
+parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, _, Max,
MaxSizes, Result) ->
parse_headers(Rest, [Octet], [lists:reverse(Header) | Headers],
- CurrSize + 1, MaxSizes, Result);
-
-parse_headers(<<?CR>> = Data, Header, Headers, CurrSize,
+ 0, Max, MaxSizes, Result);
+parse_headers(<<?CR>> = Data, Header, Headers, Current, Max,
MaxSizes, Result) ->
- {?MODULE, parse_headers, [Data, Header, Headers, CurrSize,
+ {?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
MaxSizes, Result]};
-parse_headers(<<?LF>>, Header, Headers, CurrSize,
+parse_headers(<<?LF>>, Header, Headers, Current, Max,
MaxSizes, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR, ?LF>>, Header, Headers, CurrSize,
+ parse_headers(<<?CR, ?LF>>, Header, Headers, Current, Max,
MaxSizes, Result);
-parse_headers(<<Octet, Rest/binary>>, Header, Headers,
- CurrSize, MaxSizes, Result) ->
- parse_headers(Rest, [Octet | Header], Headers, CurrSize + 1,
+parse_headers(<<Octet, Rest/binary>>, Header, Headers, Current,
+ Max, MaxSizes, Result) ->
+ parse_headers(Rest, [Octet | Header], Headers, Current + 1, Max,
MaxSizes, Result).
whole_body(Body, Length) ->
@@ -326,6 +300,14 @@ validate_path([".." | Rest], N, RequestURI) ->
validate_path([_ | Rest], N, RequestURI) ->
validate_path(Rest, N + 1, RequestURI).
+validate_version("HTTP/1.1") ->
+ true;
+validate_version("HTTP/1.0") ->
+ true;
+validate_version("HTTP/0.9") ->
+ true;
+validate_version(_) ->
+ false.
%%----------------------------------------------------------------------
%% There are 3 possible forms of the reuqest URI
%%
@@ -430,3 +412,5 @@ tag([$:|Rest], Tag) ->
tag([Chr|Rest], Tag) ->
tag(Rest, [Chr|Tag]).
+lowest_version()->
+ "HTTP/0.9".
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index bd37066ff6..b3c9cbc46a 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-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -123,7 +123,8 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
{_, Status} = httpd_manager:new_connection(Manager),
- MFA = {httpd_request, parse, [{MaxURISize, MaxHeaderSize}]},
+ MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
+ {max_version, ?HTTP_MAX_VERSION_STRING}, {max_method, ?HTTP_MAX_METHOD_STRING}]]},
State = #state{mod = Mod,
manager = Manager,
@@ -207,23 +208,15 @@ handle_info({Proto, Socket, Data},
set_new_data_size(cancel_request_timeout(State), NewDataSize)
end,
handle_http_msg(Result, NewState);
-
- {error, {uri_too_long, MaxSize}, Version} ->
- NewModData = ModData#mod{http_version = Version},
- httpd_response:send_status(NewModData, 414, "URI too long"),
- Reason = io_lib:format("Uri too long, max size is ~p~n",
- [MaxSize]),
- error_log(Reason, NewModData),
- {stop, normal, State#state{response_sent = true,
- mod = NewModData}};
- {error, {header_too_long, MaxSize}, Version} ->
+ {error, {too_long, MaxSize, ErrCode, ErrStr}, Version} ->
NewModData = ModData#mod{http_version = Version},
- httpd_response:send_status(NewModData, 413, "Header too long"),
- Reason = io_lib:format("Header too long, max size is ~p~n",
- [MaxSize]),
+ httpd_response:send_status(NewModData, ErrCode, ErrStr),
+ Reason = io_lib:format("~p: ~p max size is ~p~n",
+ [ErrCode, ErrStr, MaxSize]),
error_log(Reason, NewModData),
{stop, normal, State#state{response_sent = true,
mod = NewModData}};
+
NewMFA ->
http_transport:setopts(SockType, Socket, [{active, once}]),
case NewDataSize of
@@ -382,6 +375,11 @@ handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body},
400, URI),
Reason = io_lib:format("Malformed syntax in URI: ~p~n", [URI]),
error_log(Reason, ModData),
+ {stop, normal, State#state{response_sent = true}};
+ {error, {bad_version, Ver}} ->
+ httpd_response:send_status(ModData#mod{http_version = "HTTP/0.9"}, 400, Ver),
+ Reason = io_lib:format("Malformed syntax version: ~p~n", [Ver]),
+ error_log(Reason, ModData),
{stop, normal, State#state{response_sent = true}}
end;
handle_http_msg({ChunkedHeaders, Body},
@@ -549,7 +547,8 @@ handle_next_request(#state{mod = #mod{connection = true} = ModData,
MaxHeaderSize = max_header_size(ModData#mod.config_db),
MaxURISize = max_uri_size(ModData#mod.config_db),
- MFA = {httpd_request, parse, [{MaxURISize, MaxHeaderSize}]},
+ MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
+ {max_version, ?HTTP_MAX_VERSION_STRING}, {max_method, ?HTTP_MAX_METHOD_STRING}]]},
TmpState = State#state{mod = NewModData,
mfa = MFA,
max_keep_alive_request = decrease(Max),
diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl
index b0b18b9c3d..0d04a75205 100644
--- a/lib/inets/src/http_server/httpd_util.erl
+++ b/lib/inets/src/http_server/httpd_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -206,9 +206,6 @@ message(413, Reason,_) ->
"Entity: " ++ html_encode(Reason);
message(414,ReasonPhrase,_) ->
"Message " ++ html_encode(ReasonPhrase) ++ ".";
-message(416,ReasonPhrase,_) ->
- html_encode(ReasonPhrase);
-
message(500,_,ConfigDB) ->
ServerAdmin=lookup(ConfigDB,server_admin,"unknown@unknown"),
"The server encountered an internal error or "
@@ -233,7 +230,9 @@ message(501,{Method, RequestURI, HTTPVersion}, _ConfigDB) ->
end;
message(503, String, _ConfigDB) ->
- "This service in unavailable due to: " ++ html_encode(String).
+ "This service in unavailable due to: " ++ html_encode(String);
+message(_, ReasonPhrase, _) ->
+ html_encode(ReasonPhrase).
maybe_encode(URI) ->
Decoded = try http_uri:decode(URI) of
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index a6dd364c2d..9eae962d03 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -110,4 +110,6 @@
{registered,[inets_sup, httpc_manager]},
%% If the "new" ssl is used then 'crypto' must be started before inets.
{applications,[kernel,stdlib]},
- {mod,{inets_app,[]}}]}.
+ {mod,{inets_app,[]}},
+ {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","runtime_tools-1.8.14",
+ "mnesia-4.12","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index dd081962cc..5499596bbd 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -17,11 +17,20 @@
%% %CopyrightEnd%
{"%VSN%",
[
- {"5.9.8", [{load_module, ftp, soft_purge, soft_purge, []}]},
+ {"5.10",
+ [{load_module, httpd, soft_purge, soft_purge, []},
+ {load_module, httpd_manager, soft_purge, soft_purge, []},
+ {load_module, httpd_request, soft_purge, soft_purge, []},
+ {load_module, httpd_request_handler, soft_purge, soft_purge,
+ []}]},
{<<"5\\..*">>,[{restart_application, inets}]}
],
[
- {"5.9.8", [{load_module, ftp, soft_purge, soft_purge, []}]},
+ {"5.10",
+ [{load_module, httpd, soft_purge, soft_purge, []},
+ {load_module, httpd_manager, soft_purge, soft_purge, []},
+ {load_module, httpd_request, soft_purge, soft_purge, []},
+ {load_module, httpd_request_handler, soft_purge, soft_purge, []}]},
{<<"5\\..*">>,[{restart_application, inets}]}
]
}.
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index c156b34406..609396273d 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2013. All Rights Reserved.
+# Copyright Ericsson AB 1997-2014. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl
index c5920a3968..d4a3f28f38 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-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -356,7 +356,10 @@ http_request(Config) when is_list(Config) ->
"HTTP/1.1",
{#http_request_h{host = "www.erlang.org", te = []},
["te: ","host:www.erlang.org"]}, <<>>} =
- parse(httpd_request, parse, [?HTTP_MAX_HEADER_SIZE], HttpHead),
+ parse(httpd_request, parse, [[{max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version, ?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING}]],
+ HttpHead),
HttpHead1 = ["GET http://www.erlang.org HTTP/1.1" ++
[?CR], [?LF, ?CR, ?LF]],
@@ -364,7 +367,9 @@ http_request(Config) when is_list(Config) ->
"http://www.erlang.org",
"HTTP/1.1",
{#http_request_h{}, []}, <<>>} =
- parse(httpd_request, parse, [?HTTP_MAX_HEADER_SIZE], HttpHead1),
+ parse(httpd_request, parse, [[{max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version, ?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING}]], HttpHead1),
HttpHead2 = ["GET http://www.erlang.org HTTP/1.1" ++
@@ -373,7 +378,9 @@ http_request(Config) when is_list(Config) ->
"http://www.erlang.org",
"HTTP/1.1",
{#http_request_h{}, []}, <<>>} =
- parse(httpd_request, parse, [?HTTP_MAX_HEADER_SIZE], HttpHead2),
+ parse(httpd_request, parse, [[{max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version, ?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING}]], HttpHead2),
%% Note the following body is not related to the headers above
HttpBody = ["<HTML>\n<HEAD>\n<TITLE> dummy </TITLE>\n</HEAD>\n<BODY>\n",
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index fe6edd504e..c535d59b9f 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,15 +27,14 @@
-include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
-include("inets_test_lib.hrl").
-
+-include("http_internal.hrl").
%% Note: This directive should only be used in test suites.
-compile(export_all).
-define(URL_START, "http://").
-define(TLS_URL_START, "https://").
-define(NOT_IN_USE_PORT, 8997).
--define(LF, $\n).
--define(HTTP_MAX_HEADER_SIZE, 10240).
+
-record(sslsocket, {fd = nil, pid = nil}).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -91,8 +90,11 @@ only_simulated() ->
[
cookie,
cookie_profile,
+ empty_set_cookie,
trace,
stream_once,
+ stream_single_chunk,
+ stream_no_length,
no_content_204,
tolerate_missing_CR,
userinfo,
@@ -104,6 +106,7 @@ only_simulated() ->
remote_socket_close,
remote_socket_close_async,
transfer_encoding,
+ transfer_encoding_identity,
redirect_loop,
redirect_moved_permanently,
redirect_multiple_choises,
@@ -296,6 +299,9 @@ trace(Config) when is_list(Config) ->
pipeline(Config) when is_list(Config) ->
Request = {url(group_name(Config), "/dummy.html", Config), []},
{ok, _} = httpc:request(get, Request, [], [], pipeline),
+
+ %% Make sure pipeline session is registerd
+ test_server:sleep(4000),
keep_alive_requests(Request, pipeline).
%%--------------------------------------------------------------------
@@ -303,6 +309,9 @@ pipeline(Config) when is_list(Config) ->
persistent_connection(Config) when is_list(Config) ->
Request = {url(group_name(Config), "/dummy.html", Config), []},
{ok, _} = httpc:request(get, Request, [], [], persistent),
+
+ %% Make sure pipeline session is registerd
+ test_server:sleep(4000),
keep_alive_requests(Request, persistent).
%%-------------------------------------------------------------------------
@@ -379,6 +388,22 @@ stream_once(Config) when is_list(Config) ->
Request2 = {url(group_name(Config), "/once_chunked.html", Config), []},
stream_test(Request2, {stream, {self, once}}).
+%%-------------------------------------------------------------------------
+stream_single_chunk() ->
+ [{doc, "Test the option stream for asynchrony requests"}].
+stream_single_chunk(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/single_chunk.html", Config), []},
+ stream_test(Request, {stream, self}).
+%%-------------------------------------------------------------------------
+stream_no_length() ->
+ [{doc, "Test the option stream for asynchrony requests with HTTP 1.0 "
+ "body end on closed connection" }].
+stream_no_length(Config) when is_list(Config) ->
+ Request1 = {url(group_name(Config), "/http_1_0_no_length_single.html", Config), []},
+ stream_test(Request1, {stream, self}),
+ Request2 = {url(group_name(Config), "/http_1_0_no_length_multiple.html", Config), []},
+ stream_test(Request2, {stream, self}).
+
%%-------------------------------------------------------------------------
redirect_multiple_choises() ->
@@ -530,6 +555,19 @@ cookie_profile(Config) when is_list(Config) ->
inets:stop(httpc, cookie_test).
%%-------------------------------------------------------------------------
+empty_set_cookie() ->
+ [{doc, "Test empty Set-Cookie header."}].
+empty_set_cookie(Config) when is_list(Config) ->
+ ok = httpc:set_options([{cookies, enabled}]),
+
+ Request0 = {url(group_name(Config), "/empty_set_cookie.html", Config), []},
+
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = httpc:request(get, Request0, [], []),
+
+ ok = httpc:set_options([{cookies, disabled}]).
+
+%%-------------------------------------------------------------------------
headers_as_is(doc) ->
["Test the option headers_as_is"];
headers_as_is(Config) when is_list(Config) ->
@@ -624,6 +662,12 @@ transfer_encoding(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
+transfer_encoding_identity(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/identity_transfer_encoding.html", Config),
+ {ok, {{_,200,_}, [_|_], "IDENTITY"}} = httpc:request(URL).
+
+%%-------------------------------------------------------------------------
+
empty_response_header() ->
[{doc, "Test the case that the HTTP server does not send any headers. Solves OTP-6830"}].
empty_response_header(Config) when is_list(Config) ->
@@ -1020,7 +1064,7 @@ stream_test(Request, To) ->
ct:fail(Msg)
end,
- Body == binary_to_list(StreamedBody).
+ Body = binary_to_list(StreamedBody).
url(http, End, Config) ->
Port = ?config(port, Config),
@@ -1199,7 +1243,10 @@ dummy_server_init(Caller, ip_comm, Inet, _) ->
{ok, ListenSocket} = gen_tcp:listen(0, [Inet | BaseOpts]),
{ok, Port} = inet:port(ListenSocket),
Caller ! {port, Port},
- dummy_ipcomm_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
+ dummy_ipcomm_server_loop({httpd_request, parse, [[{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING}]]},
[], ListenSocket);
dummy_server_init(Caller, ssl, Inet, SSLOptions) ->
@@ -1211,7 +1258,10 @@ dummy_ssl_server_init(Caller, BaseOpts, Inet) ->
{ok, ListenSocket} = ssl:listen(0, [Inet | BaseOpts]),
{ok, {_, Port}} = ssl:sockname(ListenSocket),
Caller ! {port, Port},
- dummy_ssl_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
+ dummy_ssl_server_loop({httpd_request, parse, [[{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING}]]},
[], ListenSocket).
dummy_ipcomm_server_loop(MFA, Handlers, ListenSocket) ->
@@ -1241,6 +1291,7 @@ dummy_ssl_server_loop(MFA, Handlers, ListenSocket) ->
From ! {stopped, self()}
after 0 ->
{ok, Socket} = ssl:transport_accept(ListenSocket),
+ ok = ssl:ssl_accept(Socket, infinity),
HandlerPid = dummy_request_handler(MFA, Socket),
ssl:controlling_process(Socket, HandlerPid),
HandlerPid ! ssl_controller,
@@ -1287,10 +1338,16 @@ handle_request(Module, Function, Args, Socket) ->
stop ->
stop;
<<>> ->
- {httpd_request, parse, [[<<>>, ?HTTP_MAX_HEADER_SIZE]]};
+ {httpd_request, parse, [[<<>>, [{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING}]]]};
Data ->
handle_request(httpd_request, parse,
- [Data |[?HTTP_MAX_HEADER_SIZE]], Socket)
+ [Data, [{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING}]], Socket)
end;
NewMFA ->
NewMFA
@@ -1609,6 +1666,13 @@ handle_uri(_,"/capital_transfer_encoding.html",_,_,Socket,_) ->
send(Socket, http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
+handle_uri(_,"/identity_transfer_encoding.html",_,_,_,_) ->
+ "HTTP/1.0 200 OK\r\n"
+ "Transfer-Encoding:identity\r\n"
+ "Content-Length:8\r\n"
+ "\r\n"
+ "IDENTITY";
+
handle_uri(_,"/cookie.html",_,_,_,_) ->
"HTTP/1.1 200 ok\r\n" ++
"set-cookie:" ++ "test_cookie=true; path=/;" ++
@@ -1616,6 +1680,12 @@ handle_uri(_,"/cookie.html",_,_,_,_) ->
"Content-Length:32\r\n\r\n"++
"<HTML><BODY>foobar</BODY></HTML>";
+handle_uri(_,"/empty_set_cookie.html",_,_,_,_) ->
+ "HTTP/1.1 200 ok\r\n" ++
+ "set-cookie: \r\n" ++
+ "Content-Length:32\r\n\r\n"++
+ "<HTML><BODY>foobar</BODY></HTML>";
+
handle_uri(_,"/missing_crlf.html",_,_,_,_) ->
"HTTP/1.1 200 ok" ++
"Content-Length:32\r\n" ++
@@ -1635,6 +1705,30 @@ handle_uri(_,"/once_chunked.html",_,_,Socket,_) ->
http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
+handle_uri(_,"/single_chunk.html",_,_,Socket,_) ->
+ Chunk = "HTTP/1.1 200 ok\r\n" ++
+ "Transfer-Encoding:Chunked\r\n\r\n" ++
+ http_chunk:encode("<HTML><BODY>fo") ++
+ http_chunk:encode("obar</BODY></HTML>") ++
+ http_chunk:encode_last(),
+ send(Socket, Chunk);
+
+handle_uri(_,"/http_1_0_no_length_single.html",_,_,Socket,_) ->
+ Body = "HTTP/1.0 200 ok\r\n"
+ "Content-type:text/plain\r\n\r\n"
+ "single packet",
+ send(Socket, Body),
+ close(Socket);
+
+handle_uri(_,"/http_1_0_no_length_multiple.html",_,_,Socket,_) ->
+ Head = "HTTP/1.0 200 ok\r\n"
+ "Content-type:text/plain\r\n\r\n"
+ "multiple packets, ",
+ send(Socket, Head),
+ %% long body to make sure it will be sent in multiple tcp packets
+ send(Socket, string:copies("other multiple packets ", 200)),
+ close(Socket);
+
handle_uri(_,"/once.html",_,_,Socket,_) ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Content-Length:32\r\n\r\n",
diff --git a/lib/inets/test/httpd_1_0.erl b/lib/inets/test/httpd_1_0.erl
index 53f23b12e0..0836c9e881 100644
--- a/lib/inets/test/httpd_1_0.erl
+++ b/lib/inets/test/httpd_1_0.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(httpd_1_0).
--export([host/4]).
+-export([host/4, trace/4]).
%%-------------------------------------------------------------------------
%% Test cases
@@ -31,3 +31,8 @@ host(Type, Port, Host, Node) ->
"GET / HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]).
+trace(Type, Port, Host, Node)->
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "TRACE / HTTP/1.0\r\n\r\n",
+ [{statuscode, 501},
+ {version, "HTTP/1.0"}]).
diff --git a/lib/inets/test/httpd_1_1.erl b/lib/inets/test/httpd_1_1.erl
index 4b2a5f619d..6a5fc4a18f 100644
--- a/lib/inets/test/httpd_1_1.erl
+++ b/lib/inets/test/httpd_1_1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-include_lib("kernel/include/file.hrl").
--export([host/4, chunked/4, expect/4, range/4, if_test/5, http_trace/4,
+-export([host/4, chunked/4, expect/4, range/4, if_test/5, trace/4,
head/4, mod_cgi_chunked_encoding_test/5]).
%% -define(all_keys_lower_case,true).
@@ -152,13 +152,13 @@ if_test(Type, Port, Host, Node, DocRoot)->
calendar:datetime_to_gregorian_seconds(FileInfo#file_info.mtime),
Mod = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
- CreatedSec-1)),
+ CreatedSec-1)),
%% Test that we get the data when the file is modified
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET / HTTP/1.1\r\nHost:" ++ Host ++
- "\r\nIf-Modified-Since:" ++
- Mod ++ "\r\n\r\n",
+ "\r\nIf-Modified-Since:" ++
+ Mod ++ "\r\n\r\n",
[{statuscode, 200}]),
Mod1 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
CreatedSec+100)),
@@ -168,74 +168,69 @@ if_test(Type, Port, Host, Node, DocRoot)->
++ Mod1 ++"\r\n\r\n",
[{statuscode, 304}]),
-
+
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET / HTTP/1.1\r\nHost:" ++ Host ++
- "\r\nIf-Modified-Since:" ++
- "AAA[...]AAAA" ++ "\r\n\r\n",
+ "\r\nIf-Modified-Since:" ++
+ "AAA[...]AAAA" ++ "\r\n\r\n",
[{statuscode, 400}]),
-
-
- Mod2 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
+
+ Mod2 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
CreatedSec+1)),
- %% Control that the If-Unmodified-Header lmits the response
- ok = httpd_test_lib:verify_request(Type,Host,Port,Node,
- "GET / HTTP/1.1\r\nHost:"
- ++ Host ++
- "\r\nIf-Unmodified-Since:" ++ Mod2
- ++ "\r\n\r\n",
- [{statuscode, 200}]),
- Mod3 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
+ %% Control that the If-Unmodified-Header lmits the response
+ ok = httpd_test_lib:verify_request(Type,Host,Port,Node,
+ "GET / HTTP/1.1\r\nHost:"
+ ++ Host ++
+ "\r\nIf-Unmodified-Since:" ++ Mod2
+ ++ "\r\n\r\n",
+ [{statuscode, 200}]),
+ Mod3 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
CreatedSec-1)),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- "GET / HTTP/1.1\r\nHost:"
- ++ Host ++
- "\r\nIf-Unmodified-Since:"++ Mod3
+ "GET / HTTP/1.1\r\nHost:"
+ ++ Host ++
+ "\r\nIf-Unmodified-Since:"++ Mod3
++"\r\n\r\n",
- [{statuscode, 412}]),
+ [{statuscode, 412}]),
- %% Control that we get the body when the etag match
+ %% Control that we get the body when the etag match
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- "GET / HTTP/1.1\r\nHost:" ++ Host
- ++"\r\n"++
- "If-Match:"++
- httpd_util:create_etag(FileInfo)++
- "\r\n\r\n",
- [{statuscode, 200}]),
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- "GET / HTTP/1.1\r\nHost:" ++
- Host ++ "\r\n"++
- "If-Match:NotEtag\r\n\r\n",
- [{statuscode, 412}]),
+ "GET / HTTP/1.1\r\nHost:" ++ Host
+ ++"\r\n"++
+ "If-Match:"++
+ httpd_util:create_etag(FileInfo)++
+ "\r\n\r\n",
+ [{statuscode, 200}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET / HTTP/1.1\r\nHost:" ++
+ Host ++ "\r\n"++
+ "If-Match:NotEtag\r\n\r\n",
+ [{statuscode, 412}]),
- %% Control the response when the if-none-match header is there
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- "GET / HTTP/1.1\r\nHost:"
- ++ Host ++"\r\n"++
- "If-None-Match:NoTaag," ++
- httpd_util:create_etag(FileInfo) ++
- "\r\n\r\n",
- [{statuscode, 304}]),
+ %% Control the response when the if-none-match header is there
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET / HTTP/1.1\r\nHost:"
+ ++ Host ++"\r\n"++
+ "If-None-Match:NoTaag," ++
+ httpd_util:create_etag(FileInfo) ++
+ "\r\n\r\n",
+ [{statuscode, 304}]),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET / HTTP/1.1\r\nHost:"
- ++ Host ++ "\r\n"++
- "If-None-Match:NotEtag,"
- "NeihterEtag\r\n\r\n",
+ ++ Host ++ "\r\n"++
+ "If-None-Match:NotEtag,"
+ "NeihterEtag\r\n\r\n",
[{statuscode,200}]),
ok.
-
-http_trace(Type, Port, Host, Node)->
+
+trace(Type, Port, Host, Node)->
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"TRACE / HTTP/1.1\r\n" ++
"Host:" ++ Host ++ "\r\n" ++
"Max-Forwards:2\r\n\r\n",
- [{statuscode, 200}]),
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- "TRACE / HTTP/1.0\r\n\r\n",
- [{statuscode, 501},
- {version, "HTTP/1.0"}]).
+ [{statuscode, 200}]).
head(Type, Port, Host, Node)->
%% mod_include
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
@@ -283,7 +278,7 @@ mod_cgi_chunked_encoding_test(Type, Port, Host, Node, [Request| Rest])->
%%--------------------------------------------------------------------
validateRangeRequest(Socket,Response,ValidBody,C,O,DE)->
receive
- {tcp,Socket,Data} ->
+ {_,Socket,Data} ->
case string:str(Data,"\r\n") of
0->
validateRangeRequest(Socket,
@@ -312,7 +307,7 @@ validateRangeRequest1(Socket, Response, ValidBody) ->
case end_of_header(Response) of
false ->
receive
- {tcp,Socket,Data} ->
+ {_,Socket,Data} ->
validateRangeRequest1(Socket, Response ++ Data,
ValidBody);
_->
@@ -331,10 +326,10 @@ validateRangeRequest2(Socket, Head, Body, ValidBody, {multiPart,Boundary})->
validateMultiPartRangeRequest(Body, ValidBody, Boundary);
false->
receive
- {tcp, Socket, Data} ->
+ {_, Socket, Data} ->
validateRangeRequest2(Socket, Head, Body ++ Data,
ValidBody, {multiPart, Boundary});
- {tcp_closed, Socket} ->
+ {_, Socket} ->
error;
_ ->
error
@@ -353,7 +348,7 @@ validateRangeRequest2(Socket, Head, Body, ValidBody, BodySize)
end;
Size when Size < BodySize ->
receive
- {tcp, Socket, Data} ->
+ {_, Socket, Data} ->
validateRangeRequest2(Socket, Head,
Body ++ Data, ValidBody, BodySize);
_ ->
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index c0d73663d3..4be20d3a69 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -26,6 +26,7 @@
-include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
-include("inets_test_lib.hrl").
%% Note: This directive should only be used in test suites.
@@ -33,6 +34,11 @@
-record(httpd_user, {user_name, password, user_data}).
-record(httpd_group, {group_name, userlist}).
+-define(MAX_HEADER_SIZE, 256).
+%% Minutes before failed auths timeout.
+-define(FAIL_EXPIRE_TIME,1).
+%% Seconds before successful auths timeout.
+-define(AUTH_TIMEOUT,5).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -42,22 +48,59 @@ suite() ->
all() ->
[
- {group, http},
- {group, http_limit}
- %%{group, https}
+ {group, http_basic},
+ {group, https_basic},
+ {group, http_limit},
+ {group, https_limit},
+ {group, http_basic_auth},
+ {group, https_basic_auth},
+ {group, http_auth_api},
+ {group, https_auth_api},
+ {group, http_auth_api_dets},
+ {group, https_auth_api_dets},
+ {group, http_auth_api_mnesia},
+ {group, https_auth_api_mnesia},
+ {group, http_htaccess},
+ {group, https_htaccess},
+ {group, http_security},
+ {group, https_security}
].
groups() ->
[
- {http, [], all_groups()},
- %%{https, [], all_groups()},
- {http_limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]},
- {http_1_1, [], [host, chunked, expect, cgi] ++ http_head() ++ http_get()},
- {http_1_0, [], [host, cgi] ++ http_head() ++ http_get()},
- {http_0_9, [], http_head() ++ http_get()}
+ {http_basic, [], basic_groups()},
+ {https_basic, [], basic_groups()},
+ {http_limit, [], [{group, limit}]},
+ {https_limit, [], [{group, limit}]},
+ {http_basic_auth, [], [{group, basic_auth}]},
+ {https_basic_auth, [], [{group, basic_auth}]},
+ {http_auth_api, [], [{group, auth_api}]},
+ {https_auth_api, [], [{group, auth_api}]},
+ {http_auth_api_dets, [], [{group, auth_api_dets}]},
+ {https_auth_api_dets, [], [{group, auth_api_dets}]},
+ {http_auth_api_mnesia, [], [{group, auth_api_mnesia}]},
+ {https_auth_api_mnesia, [], [{group, auth_api_mnesia}]},
+ {http_htaccess, [], [{group, htaccess}]},
+ {https_htaccess, [], [{group, htaccess}]},
+ {http_security, [], [{group, security}]},
+ {https_security, [], [{group, security}]},
+ {limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]},
+ {basic_auth, [], [basic_auth_1_1, basic_auth_1_0, basic_auth_0_9]},
+ {auth_api, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
+ ]},
+ {auth_api_dets, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
+ ]},
+ {auth_api_mnesia, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
+ ]},
+ {htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]},
+ {security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code
+ {http_1_1, [], [host, chunked, expect, cgi, cgi_chunked_encoding_test,
+ trace, range, if_modified_since] ++ http_head() ++ http_get() ++ load()},
+ {http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()},
+ {http_0_9, [], http_head() ++ http_get() ++ load()}
].
-all_groups ()->
+basic_groups ()->
[{group, http_1_1},
{group, http_1_0},
{group, http_0_9}
@@ -66,15 +109,27 @@ all_groups ()->
http_head() ->
[head].
http_get() ->
- [alias, get,
- basic_auth,
- esi, ssi].
+ [alias,
+ get,
+ %%actions, Add configuration so that this test mod_action
+ esi,
+ ssi,
+ content_length,
+ bad_hex,
+ missing_CR,
+ max_header,
+ ipv6
+ ].
+load() ->
+ [light, medium
+ %%,heavy
+ ].
+
init_per_suite(Config) ->
PrivDir = ?config(priv_dir, Config),
DataDir = ?config(data_dir, Config),
inets_test_lib:stop_apps([inets]),
- inets_test_lib:start_apps([inets]),
ServerRoot = filename:join(PrivDir, "server_root"),
inets_test_lib:del_dirs(ServerRoot),
DocRoot = filename:join(ServerRoot, "htdocs"),
@@ -82,44 +137,94 @@ init_per_suite(Config) ->
[{server_root, ServerRoot},
{doc_root, DocRoot},
{node, node()},
- {host, inets_test_lib:hostname()} | Config].
+ {host, inets_test_lib:hostname()},
+ {address, getaddr()} | Config].
end_per_suite(_Config) ->
ok.
%%--------------------------------------------------------------------
-init_per_group(https = Group, Config0) ->
- case start_apps(Group) of
- ok ->
- init_httpd(Group, [{type, ssl} | Config0]);
- _ ->
- {skip, "Could not start https apps"}
- end;
-
-init_per_group(Group, Config0) when Group == http; Group == http_limit ->
+init_per_group(Group, Config0) when Group == https_basic;
+ Group == https_limit;
+ Group == https_basic_auth;
+ Group == https_auth_api;
+ Group == https_auth_api_dets;
+ Group == https_auth_api_mnesia;
+ Group == https_security
+ ->
+ init_ssl(Group, Config0);
+init_per_group(Group, Config0) when Group == http_basic;
+ Group == http_limit;
+ Group == http_basic_auth;
+ Group == http_auth_api;
+ Group == http_auth_api_dets;
+ Group == http_auth_api_mnesia;
+ Group == http_security
+ ->
+ ok = start_apps(Group),
init_httpd(Group, [{type, ip_comm} | Config0]);
init_per_group(http_1_1, Config) ->
[{http_version, "HTTP/1.1"} | Config];
init_per_group(http_1_0, Config) ->
[{http_version, "HTTP/1.0"} | Config];
init_per_group(http_0_9, Config) ->
- [{http_version, "HTTP/0.9"} | Config];
+ case {os:type(), os:version()} of
+ {{win32, _}, {5,1,2600}} ->
+ {skip, "eaddrinuse XP problem"};
+ _ ->
+ [{http_version, "HTTP/0.9"} | Config]
+ end;
+init_per_group(http_htaccess = Group, Config) ->
+ Path = ?config(doc_root, Config),
+ catch remove_htaccess(Path),
+ create_htaccess_data(Path, ?config(address, Config)),
+ ok = start_apps(Group),
+ init_httpd(Group, [{type, ip_comm} | Config]);
+init_per_group(https_htaccess = Group, Config) ->
+ Path = ?config(doc_root, Config),
+ catch remove_htaccess(Path),
+ create_htaccess_data(Path, ?config(address, Config)),
+ init_ssl(Group, Config);
+init_per_group(auth_api, Config) ->
+ [{auth_prefix, ""} | Config];
+init_per_group(auth_api_dets, Config) ->
+ [{auth_prefix, "dets_"} | Config];
+init_per_group(auth_api_mnesia, Config) ->
+ start_mnesia(?config(node, Config)),
+ [{auth_prefix, "mnesia_"} | Config];
init_per_group(_, Config) ->
Config.
-end_per_group(http, _Config) ->
- ok;
-end_per_group(https, _Config) ->
- ssl:stop();
+
+end_per_group(Group, _Config) when Group == http_basic;
+ Group == http_limit;
+ Group == http_basic_auth;
+ Group == http_auth_api;
+ Group == http_auth_api_dets;
+ Group == http_auth_api_mnesia;
+ Group == http_htaccess;
+ Group == http_security
+ ->
+ inets:stop();
+end_per_group(Group, _Config) when Group == https_basic;
+ Group == https_limit;
+ Group == https_basic_auth;
+ Group == https_auth_api;
+ Group == http_auth_api_dets;
+ Group == http_auth_api_mnesia;
+ Group == https_htaccess;
+ Group == http_security
+ ->
+ ssl:stop(),
+ inets:stop();
+
+end_per_group(auth_api_mnesia, _) ->
+ cleanup_mnesia();
+
end_per_group(_, _) ->
ok.
-init_httpd(Group, Config0) ->
- Config1 = proplists:delete(port, Config0),
- Config = proplists:delete(server_pid, Config1),
- {Pid, Port} = server_start(Group, server_config(Group, Config)),
- [{server_pid, Pid}, {port, Port} | Config].
%%--------------------------------------------------------------------
-init_per_testcase(host, Config) ->
+init_per_testcase(Case, Config) when Case == host; Case == trace ->
Prop = ?config(tc_group_properties, Config),
Name = proplists:get_value(name, Prop),
Cb = case Name of
@@ -129,15 +234,15 @@ init_per_testcase(host, Config) ->
httpd_1_1
end,
[{version_cb, Cb} | proplists:delete(version_cb, Config)];
+
+init_per_testcase(range, Config) ->
+ DocRoot = ?config(doc_root, Config),
+ create_range_data(DocRoot),
+ Config;
+
init_per_testcase(_, Config) ->
Config.
-%% init_per_testcase(basic_auth = Case, Config) ->
-%% start_mnesia(?config(node, Config)),
-%% common_init_per_test_case(Case, Config);
-
-%% end_per_testcase(basic_auth, Config) ->
-%% cleanup_mnesia();
end_per_testcase(_Case, _Config) ->
ok.
@@ -163,8 +268,11 @@ get() ->
get(Config) when is_list(Config) ->
Version = ?config(http_version, Config),
Host = ?config(host, Config),
+ Type = ?config(type, Config),
ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ ?config(port, Config),
+ transport_opts(Type, Config),
+ ?config(node, Config),
http_request("GET /index.html ", Version, Host),
[{statuscode, 200},
{header, "Content-Type", "text/html"},
@@ -172,6 +280,15 @@ get(Config) when is_list(Config) ->
{header, "Server"},
{version, Version}]).
+basic_auth_1_1(Config) when is_list(Config) ->
+ basic_auth([{http_version, "HTTP/1.1"} | Config]).
+
+basic_auth_1_0(Config) when is_list(Config) ->
+ basic_auth([{http_version, "HTTP/1.0"} | Config]).
+
+basic_auth_0_9(Config) when is_list(Config) ->
+ basic_auth([{http_version, "HTTP/0.9"} | Config]).
+
basic_auth() ->
[{doc, "Test Basic authentication with WWW-Authenticate header"}].
@@ -203,13 +320,211 @@ basic_auth(Config) ->
Config, [{statuscode, 200}]),
%% Authentication still required!
basic_auth_requiered(Config).
-
+
+auth_api_1_1(Config) when is_list(Config) ->
+ auth_api([{http_version, "HTTP/1.1"} | Config]).
+
+auth_api_1_0(Config) when is_list(Config) ->
+ auth_api([{http_version, "HTTP/1.0"} | Config]).
+
+auth_api_0_9(Config) when is_list(Config) ->
+ auth_api([{http_version, "HTTP/0.9"} | Config]).
+
+auth_api() ->
+ [{doc, "Test mod_auth API"}].
+
+auth_api(Config) when is_list(Config) ->
+ Prefix = ?config(auth_prefix, Config),
+ do_auth_api(Prefix, Config).
+
+do_auth_api(AuthPrefix, Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ Port = ?config(port, Config),
+ Node = ?config(node, Config),
+ ServerRoot = ?config(server_root, Config),
+ ok = http_status("GET / ", Config,
+ [{statuscode, 200}]),
+ ok = auth_status(auth_request("/", "one", "WrongPassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Make sure Authenticate header is received even the second time
+ %% we try a incorrect password! Otherwise a browser client will hang!
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "dummy", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/", "dummy", "WrongPassword",
+ Version, Host), Config, [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+
+ %% Change the password to DummyPassword then try to add a user
+ %% Get an error and set it to NoPassword
+ ok = update_password(Node, ServerRoot, Host, Port, AuthPrefix,
+ "open", "NoPassword", "DummyPassword"),
+ {error,bad_password} =
+ add_user(Node, ServerRoot, Port, AuthPrefix, "open", "one",
+ "onePassword", []),
+ ok = update_password(Node, ServerRoot, Host, Port, AuthPrefix, "open",
+ "DummyPassword", "NoPassword"),
+
+ %% Test /*open, require user one Aladdin
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "open"),
+
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "one", "onePassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "Aladdin", "onePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+
+ true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "one",
+ "onePassword", []),
+ true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "two",
+ "twoPassword", []),
+ true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "Aladdin",
+ "AladdinPassword", []),
+ {ok, [_|_]} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "open"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "one", "WrongPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "one", "onePassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "two", "twoPassword", Version, Host),
+ Config,[{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "Aladdin", "WrongPassword", Version, Host),
+ Config,[{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "Aladdin", "AladdinPassword", Version, Host),
+ Config, [{statuscode, 200}]),
+
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "open"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "open"),
+
+ %% Phase 2
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port, AuthPrefix,
+ "secret"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "one", "onePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "three", "threePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret", "one",
+ "onePassword",
+ []),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret",
+ "two", "twoPassword", []),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret", "Aladdin",
+ "AladdinPassword",[]),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret",
+ "one", "group1"),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret",
+ "two", "group1"),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix,
+ "secret", "Aladdin", "group2"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "one", "onePassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "two", "twoPassword", Version, Host),
+ Config,[{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "Aladdin", "AladdinPassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "three", "threePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "secret"),
+ remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
+
+ {ok, []} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
+
+ %% Phase 3
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/",
+ "three", "threePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ add_user(Node, ServerRoot, Port, AuthPrefix,
+ "secret/top_secret","three",
+ "threePassword",[]),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret",
+ "two","twoPassword", []),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret", "three", "group3"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "three", "threePassword",
+ Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret", "two", "group3"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/",
+ "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "secret/top_secret"),
+ remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ {ok, []} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/","three", "threePassword", Version, Host),
+ Config, [{statuscde, 401}]).
+%%-------------------------------------------------------------------------
+ipv6() ->
+ [{require, ipv6_hosts},
+ {doc,"Test ipv6."}].
+ipv6(Config) when is_list(Config) ->
+ {ok, Hostname0} = inet:gethostname(),
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ URI = http_request("GET /", Version, Host),
+ httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config), [inet6],
+ ?config(code, Config),
+ URI,
+ [{statuscode, 200}, {version, Version}]);
+ false ->
+ {skip, "Host does not support IPv6"}
+ end.
+
+%%-------------------------------------------------------------------------
ssi() ->
[{doc, "HTTP GET server side include test"}].
ssi(Config) when is_list(Config) ->
Version = ?config(http_version, Config),
Host = ?config(host, Config),
+ Type = ?config(type, Config),
ok = httpd_test_lib:verify_request(?config(type, Config), Host, ?config(port, Config),
+ transport_opts(Type, Config),
?config(node, Config),
http_request("GET /fsize.shtml ", Version, Host),
[{statuscode, 200},
@@ -217,6 +532,131 @@ ssi(Config) when is_list(Config) ->
{header, "Date"},
{header, "Server"},
{version, Version}]).
+%%-------------------------------------------------------------------------
+htaccess_1_1(Config) when is_list(Config) ->
+ htaccess([{http_version, "HTTP/1.1"} | Config]).
+
+htaccess_1_0(Config) when is_list(Config) ->
+ htaccess([{http_version, "HTTP/1.0"} | Config]).
+
+htaccess_0_9(Config) when is_list(Config) ->
+ htaccess([{http_version, "HTTP/0.9"} | Config]).
+
+htaccess() ->
+ [{doc, "Test mod_auth API"}].
+
+htaccess(Config) when is_list(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ Type = ?config(type, Config),
+ Port = ?config(port, Config),
+ Node = ?config(node, Config),
+ %% Control that authentication required!
+ %% Control that the pages that shall be
+ %% authenticated really need authenticatin
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/open/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/top_secret/ ",
+ Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+
+ %% Make sure Authenticate header is received even the second time
+ %% we try a incorrect password! Otherwise a browser client will hang!
+ ok = auth_status(auth_request("/ht/open/",
+ "dummy", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+ ok = auth_status(auth_request("/ht/open/",
+ "dummy", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+
+ %% Control that not just the first user in the list is valid
+ %% Control the first user
+ %% Authennticating ["one:OnePassword" user first in user list]
+ ok = auth_status(auth_request("/ht/open/dummy.html", "one", "OnePassword",
+ Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Control the second user
+ %% Authentication OK and a directory listing is supplied!
+ %% ["Aladdin:open sesame" user second in user list]
+ ok = auth_status(auth_request("/ht/open/","Aladdin",
+ "AladdinPassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Contro that bad passwords and userids get a good denial
+ %% User correct but wrong password! ["one:one" user first in user list]
+ ok = auth_status(auth_request("/ht/open/", "one", "one", Version, Host), Config,
+ [{statuscode, 401}]),
+ %% Neither user or password correct! ["dummy:dummy"]
+ ok = auth_status(auth_request("/ht/open/", "dummy", "dummy", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% Control that authetication still works, even if its a member in a group
+ %% Authentication OK! ["two:TwoPassword" user in first group]
+ ok = auth_status(auth_request("/ht/secret/dummy.html", "two",
+ "TwoPassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Authentication OK and a directory listing is supplied!
+ %% ["three:ThreePassword" user in second group]
+ ok = auth_status(auth_request("/ht/secret/", "three",
+ "ThreePassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Deny users with bad passwords even if the user is a group member
+ %% User correct but wrong password! ["two:two" user in first group]
+ ok = auth_status(auth_request("/ht/secret/", "two", "two", Version, Host), Config,
+ [{statuscode, 401}]),
+ %% Neither user or password correct! ["dummy:dummy"]
+ ok = auth_status(auth_request("/ht/secret/", "dummy", "dummy", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% control that we deny the users that are in subnet above the allowed
+ ok = auth_status(auth_request("/ht/blocknet/dummy.html", "four",
+ "FourPassword", Version, Host), Config,
+ [{statuscode, 403}]),
+ %% Control that we only applies the rules to the right methods
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("HEAD /ht/blocknet/dummy.html ", Version, Host),
+ [{statuscode, head_status(Version)},
+ {version, Version}]),
+
+ %% Control that the rerquire directive can be overrideen
+ ok = auth_status(auth_request("/ht/secret/top_secret/ ", "Aladdin", "AladdinPassword",
+ Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% Authentication still required!
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/open/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/top_secret/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]).
+
+%%-------------------------------------------------------------------------
host() ->
[{doc, "Test host header"}].
@@ -224,21 +664,21 @@ host(Config) when is_list(Config) ->
Cb = ?config(version_cb, Config),
Cb:host(?config(type, Config), ?config(port, Config),
?config(host, Config), ?config(node, Config)).
-
+%%-------------------------------------------------------------------------
chunked() ->
[{doc, "Check that the server accepts chunked requests."}].
chunked(Config) when is_list(Config) ->
httpd_1_1:chunked(?config(type, Config), ?config(port, Config),
?config(host, Config), ?config(node, Config)).
-
+%%-------------------------------------------------------------------------
expect() ->
["Check that the server handles request with the expect header "
"field appropiate"].
expect(Config) when is_list(Config) ->
httpd_1_1:expect(?config(type, Config), ?config(port, Config),
?config(host, Config), ?config(node, Config)).
-
+%%-------------------------------------------------------------------------
max_clients_1_1() ->
[{doc, "Test max clients limit"}].
@@ -256,7 +696,7 @@ max_clients_0_9() ->
max_clients_0_9(Config) when is_list(Config) ->
do_max_clients([{http_version, "HTTP/0.9"} | Config]).
-
+%%-------------------------------------------------------------------------
esi() ->
[{doc, "Test mod_esi"}].
@@ -286,7 +726,7 @@ esi(Config) when is_list(Config) ->
ok = http_status("GET /cgi-bin/erl/httpd_example:get ",
Config, [{statuscode, 200},
{no_header, "cache-control"}]).
-
+%%-------------------------------------------------------------------------
cgi() ->
[{doc, "Test mod_cgi"}].
@@ -361,7 +801,27 @@ cgi(Config) when is_list(Config) ->
ok = http_status("GET /cgi-bin/" ++ Script ++ " ", Config,
[{statuscode, 200},
{no_header, "cache-control"}]).
-
+%%-------------------------------------------------------------------------
+cgi_chunked_encoding_test() ->
+ [{doc, "Test chunked encoding together with mod_cgi "}].
+cgi_chunked_encoding_test(Config) when is_list(Config) ->
+ Host = ?config(host, Config),
+ Script =
+ case test_server:os_type() of
+ {win32, _} ->
+ "/cgi-bin/printenv.bat";
+ _ ->
+ "/cgi-bin/printenv.sh"
+ end,
+ Requests =
+ ["GET " ++ Script ++ " HTTP/1.1\r\nHost:"++ Host ++"\r\n\r\n",
+ "GET /cgi-bin/erl/httpd_example/newformat HTTP/1.1\r\nHost:"
+ ++ Host ++"\r\n\r\n"],
+ httpd_1_1:mod_cgi_chunked_encoding_test(?config(type, Config), ?config(port, Config),
+ Host,
+ ?config(node, Config),
+ Requests).
+%%-------------------------------------------------------------------------
alias() ->
[{doc, "Test mod_alias"}].
@@ -389,160 +849,246 @@ alias(Config) when is_list(Config) ->
[{statuscode, 301},
{header, "Location"},
{header, "Content-Type","text/html"}]).
+%%-------------------------------------------------------------------------
+actions() ->
+ [{doc, "Test mod_actions"}].
+actions(Config) when is_list(Config) ->
+ ok = http_status("GET /", Config, [{statuscode, 200}]).
-%% auth_api() ->
-%% [{doc, "Test mod_auth API"}].
-
-%% auth_api(Config) when is_list(Config) ->
-%% Version = ?config(http_version, Config),
-%% Host = ?config(host, Config),
-%% ok = http_status("GET / ", Config,
-%% [{statuscode, 200}]),
-%% ok = auth_status(auth_request("/", "one", "WrongPassword", Version, Host), Config,
-%% [{statuscode, 200}]),
-
-%% %% Make sure Authenticate header is received even the second time
-%% %% we try a incorrect password! Otherwise a browser client will hang!
-%% ok = auth_status(auth_request("/" ++ AuthStoreType ++ "open/",
-%% "dummy", "WrongPassword", Host), Config,
-%% [{statuscode, 401},
-%% {header, "WWW-Authenticate"}]),
-%% ok = auth_status(auth_request("/" ++ AuthStoreType ++ "open/", "dummy", "WrongPassword",
-%% Host), Config, [{statuscode, 401},
-%% {header, "WWW-Authenticate"}]),
-
-%% %% Change the password to DummyPassword then try to add a user
-%% %% Get an error and set it to NoPassword
-%% ok = update_password(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "open", "NoPassword", "DummyPassword"),
-%% {error,bad_password} =
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "one",
-%% "onePassword", []),
-%% ok = update_password(Node, ServerRoot, Host, Port, AuthStoreType ++"open",
-%% "DummyPassword", "NoPassword"),
-
-%% %% Test /*open, require user one Aladdin
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++ "open"),
+%%-------------------------------------------------------------------------
+range() ->
+ [{doc, "Test Range header"}].
+
+range(Config) when is_list(Config) ->
+ httpd_1_1:range(?config(type, Config), ?config(port, Config),
+ ?config(host, Config), ?config(node, Config)).
+
+%%-------------------------------------------------------------------------
+if_modified_since() ->
+ [{doc, "Test If-Modified-Since header"}].
+
+if_modified_since(Config) when is_list(Config) ->
+ httpd_1_1:if_test(?config(type, Config), ?config(port, Config),
+ ?config(host, Config), ?config(node, Config),
+ ?config(doc_root, Config)).
+%%-------------------------------------------------------------------------
+trace() ->
+ [{doc, "Test TRACE method"}].
+
+trace(Config) when is_list(Config) ->
+ Cb = ?config(version_cb, Config),
+ Cb:trace(?config(type, Config), ?config(port, Config),
+ ?config(host, Config), ?config(node, Config)).
+
+%%-------------------------------------------------------------------------
+light() ->
+ ["Test light load"].
+light(Config) when is_list(Config) ->
+ httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
+ ?config(node, Config), 10).
+%%-------------------------------------------------------------------------
+medium() ->
+ ["Test medium load"].
+medium(Config) when is_list(Config) ->
+ httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
+ ?config(node, Config), 100).
+%%-------------------------------------------------------------------------
+heavy() ->
+ ["Test heavy load"].
+heavy(Config) when is_list(Config) ->
+ httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
+ ?config(node, Config),
+ 1000).
+%%-------------------------------------------------------------------------
+content_length() ->
+ ["Tests that content-length is correct OTP-5775"].
+content_length(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ ok = httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config), ?config(node, Config),
+ http_request("GET /cgi-bin/erl/httpd_example:get_bin ",
+ Version, Host),
+ [{statuscode, 200},
+ {content_length, 274},
+ {version, Version}]).
+%%-------------------------------------------------------------------------
+bad_hex() ->
+ ["Tests that a URI with a bad hexadecimal code is handled OTP-6003"].
+bad_hex(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ ok = httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config), ?config(node, Config),
+ http_request("GET http://www.erlang.org/%skalle ",
+ Version, Host),
+ [{statuscode, 400},
+ {version, Version}]).
+%%-------------------------------------------------------------------------
+missing_CR() ->
+ ["Tests missing CR in delimiter OTP-7304"].
+missing_CR(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ ok = httpd_test_lib:verify_request(?config(type, Config), Host,
+ ?config(port, Config), ?config(node, Config),
+ http_request_missing_CR("GET /index.html ", Version, Host),
+ [{statuscode, 200},
+ {version, Version}]).
+
+%%-------------------------------------------------------------------------
+max_header() ->
+ ["Denial Of Service (DOS) attack, prevented by max_header"].
+max_header(Config) when is_list(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ case Version of
+ "HTTP/0.9" ->
+ {skip, no_implemented};
+ _ ->
+ dos_hostname(?config(type, Config), ?config(port, Config), Host,
+ ?config(node, Config), Version, ?MAX_HEADER_SIZE)
+ end.
+
+%%-------------------------------------------------------------------------
+security_1_1(Config) when is_list(Config) ->
+ security([{http_version, "HTTP/1.1"} | Config]).
+
+security_1_0(Config) when is_list(Config) ->
+ security([{http_version, "HTTP/1.0"} | Config]).
+
+security() ->
+ ["Test mod_security"].
+security(Config) ->
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ Port = ?config(port, Config),
+ Node = ?config(node, Config),
+ ServerRoot = ?config(server_root, Config),
+
+ global:register_name(mod_security_test, self()), % Receive events
+
+ test_server:sleep(5000),
+
+ OpenDir = filename:join([ServerRoot, "htdocs", "open"]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
-%% "one", "onePassword", [{statuscode, 401}]),
+ %% Test blocking / unblocking of users.
+
+ %% /open, require user one Aladdin
+ remove_users(Node, ServerRoot, Host, Port, "", "open"),
+
+ ok = auth_status(auth_request("/open/",
+ "one", "onePassword", Version, Host), Config,
+ [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
-%% "two", "twoPassword", [{statuscode, 401}]),
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "one"}, {password, "onePassword"}]},
+ Node, Port),
+
+ ok = auth_status(auth_request("/open/",
+ "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
-%% "Aladdin", "onePassword", [{statuscode, 401}]),
-
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "one",
-%% "onePassword", []),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "two",
-%% "twoPassword", []),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "Aladdin",
-%% "AladdinPassword", []),
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "two"}, {password, "twoPassword"}]},
+ Node, Port),
+
+ ok = auth_status(auth_request("/open/",
+ "Aladdin", "AladdinPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "Aladdin"},
+ {password, "AladdinPassword"}]},
+ Node, Port),
+
+ add_user(Node, ServerRoot, Port, "", "open", "one", "onePassword", []),
+ add_user(Node, ServerRoot, Port, "", "open", "two", "twoPassword", []),
+
+ ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "one"}, {password, "WrongPassword"}]},
+ Node, Port),
+
+ ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "one"}, {password, "WrongPassword"}]},
+ Node, Port),
+ receive_security_event({event, user_block, Port, OpenDir,
+ [{user, "one"}]}, Node, Port),
+
+ global:unregister_name(mod_security_test), % No more events.
+
+ ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% User "one" should be blocked now..
+ case list_blocked_users(Node, Port) of
+ [{"one",_, Port, OpenDir,_}] ->
+ ok;
+ Blocked ->
+ ct:fail({unexpected_blocked, Blocked})
+ end,
+
+ [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir),
+
+ true = unblock_user(Node, "one", Port, OpenDir),
+ %% User "one" should not be blocked any more.
+
+ [] = list_blocked_users(Node, Port),
+
+ ok = auth_status(auth_request("/open/", "one", "onePassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Test list_auth_users & auth_timeout
+
+ ["one"] = list_auth_users(Node, Port),
+
+ ok = auth_status(auth_request("/open/", "two", "onePassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ ["one"] = list_auth_users(Node, Port),
+
-%% {ok, [_|_]} = list_users(Node, ServerRoot, Host, Port,
-%% AuthStoreType++"open"),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/",
-%% "one", "WrongPassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/",
-%% "one", "onePassword", [{statuscode, 200}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
-%% "two", "twoPassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/",
-%% "Aladdin", "WrongPassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/",
-%% "Aladdin", "AladdinPassword", [{statuscode, 200}]),
+ ["one"] = list_auth_users(Node, Port, OpenDir),
+
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType++"open"),
-%% {ok, []} = list_users(Node, ServerRoot, Host, Port,
-%% AuthStoreType++"open"),
-
-%% %% Phase 2
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType++"secret"),
-%% {ok, []} = list_users(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "secret"),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "one", "onePassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "two", "twoPassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "secret/",
-%% "three", "threePassword", [{statuscode, 401}]),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret", "one",
-%% "onePassword",
-%% []),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret",
-%% "two", "twoPassword", []),
-%% add_user(Node, ServerRoot, Port, AuthStoreType++"secret", "Aladdin",
-%% "AladdinPassword",[]),
-%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++ "secret",
-%% "one", "group1"),
-%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++ "secret",
-%% "two", "group1"),
-%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++
-%% "secret", "Aladdin", "group2"),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "one", "onePassword", [{statuscode, 200}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "two", "twoPassword", [{statuscode, 200}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "Aladdin", "AladdinPassword", [{statuscode, 200}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/",
-%% "three", "threePassword", [{statuscode, 401}]),
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++ "secret"),
-%% {ok, []} = list_users(Node, ServerRoot, Host, Port,
-%% AuthStoreType ++ "secret"),
-%% remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++ "secret"),
-%% Directory = filename:join([ServerRoot, "htdocs", AuthStoreType ++
-%% "secret"]),
-%% {ok, []} = list_groups(Node, ServerRoot, Host, Port, Directory),
-
-%% %% Phase 3
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "secret/top_secret"),
-%% remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "secret/top_secret"),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
-%% "secret/top_secret/",
-%% "three", "threePassword", [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
-%% "secret/top_secret/", "two", "twoPassword",
-%% [{statuscode, 401}]),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++
-%% "secret/top_secret","three",
-%% "threePassword",[]),
-%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret/top_secret",
-%% "two","twoPassword", []),
-%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++
-%% "secret/top_secret",
-%% "three", "group3"),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
-%% "secret/top_secret/", "three", "threePassword",
-%% [{statuscode, 200}]),
-%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++
-%% "secret/top_secret/", "two", "twoPassword",
-%% [{statuscode, 401}]),
-%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++
-%% "secret/top_secret",
-%% "two", "group3"),
-%% auth_request(Type,Host,Port,Node,"/" ++ AuthStoreType ++
-%% "secret/top_secret/",
-%% "two", "twoPassword", [{statuscode, 200}]),
-%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "secret/top_secret"),
-%% {ok, []} = list_users(Node, ServerRoot, Host, Port,
-%% AuthStoreType ++ "secret/top_secret"),
-%% remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++
-%% "secret/top_secret"),
-%% Directory2 = filename:join([ServerRoot, "htdocs",
-%% AuthStoreType ++ "secret/top_secret"]),
-%% {ok, []} = list_groups(Node, ServerRoot, Host, Port, Directory2),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++
-%% "secret/top_secret/", "two", "twoPassword",
-%% [{statuscode, 401}]),
-%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++
-%% "secret/top_secret/","three", "threePassword",
-%% [{statuscode, 401}]).
+ ok = auth_status(auth_request("/open/", "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ ["one"] = list_auth_users(Node, Port),
+
+
+ ["one"] = list_auth_users(Node, Port, OpenDir),
+
+ %% Wait for successful auth to timeout.
+ test_server:sleep(?AUTH_TIMEOUT*1001),
+
+ [] = list_auth_users(Node, Port),
+
+ [] = list_auth_users(Node, Port, OpenDir),
+
+ %% "two" is blocked.
+
+ true = unblock_user(Node, "two", Port, OpenDir),
+
+
+ %% Test explicit blocking. Block user 'two'.
+
+ [] = list_blocked_users(Node,Port,OpenDir),
+
+ true = block_user(Node, "two", Port, OpenDir, 10),
+
+ ok = auth_status(auth_request("/open/", "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ true = unblock_user(Node, "two", Port, OpenDir).
+
%%--------------------------------------------------------------------
@@ -550,21 +1096,35 @@ alias(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
do_max_clients(Config) ->
Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- start_blocker(Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("GET /index.html ", Version, Host),
+ Host = ?config(host, Config),
+ Port = ?config(port, Config),
+ Type = ?config(type, Config),
+
+ Request = http_request("GET /index.html ", Version, Host),
+ BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
+ inets_test_lib:send(Type, Socket, BlockRequest),
+ ct:sleep(100), %% Avoid possible timing issues
+ ok = httpd_test_lib:verify_request(Type, Host,
+ Port,
+ transport_opts(Type, Config),
+ ?config(node, Config),
+ Request,
[{statuscode, 503},
{version, Version}]),
receive
- after 2000 ->
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("GET /index.html ", Version, Host),
- [{statuscode, 200},
- {version, Version}])
- end.
+ {_, Socket, _Msg} ->
+ ok
+ end,
+ inets_test_lib:close(Type, Socket),
+ ct:sleep(100), %% Avoid possible timing issues
+ ok = httpd_test_lib:verify_request(Type, Host,
+ Port,
+ transport_opts(Type, Config),
+ ?config(node, Config),
+ Request,
+ [{statuscode, 200},
+ {version, Version}]).
setup_server_dirs(ServerRoot, DocRoot, DataDir) ->
CgiDir = filename:join(ServerRoot, "cgi-bin"),
@@ -604,10 +1164,24 @@ setup_server_dirs(ServerRoot, DocRoot, DataDir) ->
ok = file:write_file_info(EnvCGI,
FileInfo1#file_info{mode = 8#00755}).
-start_apps(https) ->
- inets_test_lib:start_apps([crypto, public_key, ssl]);
-start_apps(_) ->
- ok.
+start_apps(Group) when Group == https_basic;
+ Group == https_limit;
+ Group == https_basic_auth;
+ Group == https_auth_api;
+ Group == https_auth_api_dets;
+ Group == https_auth_api_mnesia;
+ Group == http_htaccess;
+ Group == http_security ->
+ inets_test_lib:start_apps([inets, asn1, crypto, public_key, ssl]);
+start_apps(Group) when Group == http_basic;
+ Group == http_limit;
+ Group == http_basic_auth;
+ Group == http_auth_api;
+ Group == http_auth_api_dets;
+ Group == http_auth_api_mnesia;
+ Group == https_htaccess;
+ Group == https_security ->
+ inets_test_lib:start_apps([inets]).
server_start(_, HttpdConfig) ->
{ok, Pid} = inets:start(httpd, HttpdConfig),
@@ -615,6 +1189,80 @@ server_start(_, HttpdConfig) ->
{value, {_, _, Info}} = lists:keysearch(Pid, 2, Serv),
{Pid, proplists:get_value(port, Info)}.
+init_ssl(Group, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ CaKey = {_Trusted,_} =
+ erl_make_certs:make_cert([{key, dsa},
+ {subject,
+ [{name, "Public Key"},
+ {?'id-at-name',
+ {printableString, "public_key"}},
+ {?'id-at-pseudonym',
+ {printableString, "pubkey"}},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}
+ ]}
+ ]),
+ ok = erl_make_certs:write_pem(PrivDir, "public_key_cacert", CaKey),
+
+ CertK1 = {_Cert1, _} = erl_make_certs:make_cert([{issuer, CaKey}]),
+ CertK2 = {_Cert2,_} = erl_make_certs:make_cert([{issuer, CertK1},
+ {digest, md5},
+ {extensions, false}]),
+ ok = erl_make_certs:write_pem(PrivDir, "public_key_cert", CertK2),
+
+ case start_apps(Group) of
+ ok ->
+ init_httpd(Group, [{type, ssl} | Config]);
+ _ ->
+ {skip, "Could not start https apps"}
+ end.
+
+server_config(http_basic, Config) ->
+ basic_conf() ++ server_config(http, Config);
+server_config(https_basic, Config) ->
+ basic_conf() ++ server_config(https, Config);
+server_config(http_limit, Config) ->
+ [{max_clients, 1}] ++ server_config(http, Config);
+server_config(https_limit, Config) ->
+ [{max_clients, 1}] ++ server_config(https, Config);
+server_config(http_basic_auth, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_conf(ServerRoot) ++ server_config(http, Config);
+server_config(https_basic_auth, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_conf(ServerRoot) ++ server_config(https, Config);
+server_config(http_auth_api, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, plain) ++ server_config(http, Config);
+server_config(https_auth_api, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, plain) ++ server_config(https, Config);
+server_config(http_auth_api_dets, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, dets) ++ server_config(http, Config);
+server_config(https_auth_api_dets, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, dets) ++ server_config(https, Config);
+server_config(http_auth_api_mnesia, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, mnesia) ++ server_config(http, Config);
+server_config(https_auth_api_mnesia, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ auth_api_conf(ServerRoot, mnesia) ++ server_config(https, Config);
+server_config(http_htaccess, Config) ->
+ auth_access_conf() ++ server_config(http, Config);
+server_config(https_htaccess, Config) ->
+ auth_access_conf() ++ server_config(https, Config);
+server_config(http_security, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(http, Config);
+server_config(https_security, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(https, Config);
+
server_config(http, Config) ->
ServerRoot = ?config(server_root, Config),
[{port, 0},
@@ -625,6 +1273,7 @@ server_config(http, Config) ->
{ipfamily, inet},
{max_header_size, 256},
{max_header_action, close},
+ {directory_index, ["index.html", "welcome.html"]},
{mime_types, [{"html","text/html"},{"htm","text/html"}, {"shtml","text/html"},
{"gif", "image/gif"}]},
{alias, {"/icons/", filename:join(ServerRoot,"icons") ++ "/"}},
@@ -633,13 +1282,24 @@ server_config(http, Config) ->
{script_alias, {"/htbin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}},
{erl_script_alias, {"/cgi-bin/erl", [httpd_example, io]}},
{eval_script_alias, {"/eval", [httpd_example, io]}}
- ] ++ auth_conf(ServerRoot);
+ ];
-server_config(http_limit, Config) ->
- [{max_clients, 1}] ++ server_config(http, Config);
-
-server_config(_, _) ->
- [].
+server_config(https, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ [{socket_type, {essl,
+ [{cacertfile,
+ filename:join(PrivDir, "public_key_cacert.pem")},
+ {certfile,
+ filename:join(PrivDir, "public_key_cert.pem")},
+ {keyfile,
+ filename:join(PrivDir, "public_key_cert_key.pem")}
+ ]}}] ++ server_config(http, Config).
+
+init_httpd(Group, Config0) ->
+ Config1 = proplists:delete(port, Config0),
+ Config = proplists:delete(server_pid, Config1),
+ {Pid, Port} = server_start(Group, server_config(Group, Config)),
+ [{server_pid, Pid}, {port, Port} | Config].
http_request(Request, "HTTP/1.1" = Version, Host, {Headers, Body}) ->
Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n" ++ Headers ++ "\r\n" ++ Body;
@@ -662,19 +1322,33 @@ auth_request(Path, User, Passwd, Version, _Host) ->
base64:encode_to_string(User++":"++Passwd) ++
"\r\n\r\n".
+http_request_missing_CR(Request, "HTTP/1.1" = Version, Host) ->
+ Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n\r\n\n";
+http_request_missing_CR(Request, Version, _) ->
+ Request ++ Version ++ "\r\n\n".
+
head_status("HTTP/0.9") ->
501; %% Not implemented in HTTP/0.9
head_status(_) ->
200.
+basic_conf() ->
+ [{modules, [mod_alias, mod_range, mod_responsecontrol,
+ mod_trace, mod_esi, mod_cgi, mod_dir, mod_get, mod_head]}].
+
+auth_access_conf() ->
+ [{modules, [mod_alias, mod_htaccess, mod_dir, mod_get, mod_head]},
+ {access_files, [".htaccess"]}].
+
auth_conf(Root) ->
- [{directory, {filename:join(Root, "htdocs/open"),
+ [{modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
+ {directory, {filename:join(Root, "htdocs/open"),
[{auth_type, plain},
{auth_name, "Open Area"},
{auth_user_file, filename:join(Root, "auth/passwd")},
{auth_group_file, filename:join(Root, "auth/group")},
{require_user, ["one", "Aladdin"]}]}},
- {directory, {filename:join(Root, "htdocs/secret"),
+ {directory, {filename:join(Root, "htdocs/secret"),
[{auth_type, plain},
{auth_name, "Secret Area"},
{auth_user_file, filename:join(Root, "auth/passwd")},
@@ -685,43 +1359,134 @@ auth_conf(Root) ->
{auth_name, "Top Secret Area"},
{auth_user_file, filename:join(Root, "auth/passwd")},
{auth_group_file, filename:join(Root, "auth/group")},
- {require_group, ["group3"]}]}},
+ {require_group, ["group3"]}]}}].
+
+auth_api_conf(Root, plain) ->
+ [{modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
{directory, {filename:join(Root, "htdocs/open"),
- [{auth_type, mnesia},
+ [{auth_type, plain},
{auth_name, "Open Area"},
{auth_user_file, filename:join(Root, "auth/passwd")},
{auth_group_file, filename:join(Root, "auth/group")},
{require_user, ["one", "Aladdin"]}]}},
{directory, {filename:join(Root, "htdocs/secret"),
- [{auth_type, mnesia},
+ [{auth_type, plain},
{auth_name, "Secret Area"},
{auth_user_file, filename:join(Root, "auth/passwd")},
{auth_group_file, filename:join(Root, "auth/group")},
- {require_group, ["group1", "group2"]}]}}
- ].
+ {require_group, ["group1", "group2"]}]}},
+ {directory, {filename:join(Root, "htdocs/secret/top_secret"),
+ [{auth_type, plain},
+ {auth_name, "Top Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group3"]}]}}];
+auth_api_conf(Root, dets) ->
+ [
+ {modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
+ {directory, {filename:join(Root, "htdocs/dets_open"),
+ [{auth_type, dets},
+ {auth_name, "Dets Open Area"},
+ {auth_user_file, filename:join(Root, "passwd")},
+ {auth_group_file, filename:join(Root, "group")},
+ {require_user, ["one", "Aladdin"]}]}},
+ {directory, {filename:join(Root, "htdocs/dets_secret"),
+ [{auth_type, dets},
+ {auth_name, "Dests Secret Area"},
+ {auth_user_file, filename:join(Root, "passwd")},
+ {auth_group_file, filename:join(Root, "group")},
+ {require_group, ["group1", "group2"]}]}},
+ {directory, {filename:join(Root, "htdocs/dets_secret/top_secret"),
+ [{auth_type, dets},
+ {auth_name, "Dets Top Secret Area"},
+ {auth_user_file, filename:join(Root, "passwd")},
+ {auth_group_file, filename:join(Root, "group")},
+ {require_group, ["group3"]}]}}
+ ];
+
+auth_api_conf(Root, mnesia) ->
+ [{modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
+ {directory, {filename:join(Root, "htdocs/mnesia_open"),
+ [{auth_type, mnesia},
+ {auth_name, "Mnesia Open Area"},
+ {require_user, ["one", "Aladdin"]}]}},
+ {directory, {filename:join(Root, "htdocs/mnesia_secret"),
+ [{auth_type, mnesia},
+ {auth_name, "Mnesia Secret Area"},
+ {require_group, ["group1", "group2"]}]}},
+ {directory, {filename:join(Root, "htdocs/mnesia_secret/top_secret"),
+ [{auth_type, mnesia},
+ {auth_name, "Mnesia Top Secret Area"},
+ {require_group, ["group3"]}]}}].
+
+security_conf(Root) ->
+ SecFile = filename:join(Root, "security_data"),
+ Open = filename:join(Root, "htdocs/open"),
+ Secret = filename:join(Root, "htdocs/secret"),
+ TopSecret = filename:join(Root, "htdocs/secret/top_secret"),
+
+ [{modules, [mod_alias, mod_auth, mod_security, mod_dir, mod_get, mod_head]},
+ {security_directory, {Open,
+ [{auth_name, "Open Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_user, ["one", "Aladdin"]} |
+ mod_security_conf(SecFile, Open)]}},
+ {security_directory, {Secret,
+ [{auth_name, "Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group1", "group2"]} |
+ mod_security_conf(SecFile, Secret)]}},
+ {security_directory, {TopSecret,
+ [{auth_name, "Top Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group3"]} |
+ mod_security_conf(SecFile, TopSecret)]}}].
+
+mod_security_conf(SecFile, Dir) ->
+ [{data_file, SecFile},
+ {max_retries, 3},
+ {fail_expire_time, ?FAIL_EXPIRE_TIME},
+ {block_time, 1},
+ {auth_timeout, ?AUTH_TIMEOUT},
+ {callback_module, ?MODULE},
+ {path, Dir} %% This is should not be needed, but is atm, awful design!
+ ].
+
http_status(Request, Config, Expected) ->
Version = ?config(http_version, Config),
Host = ?config(host, Config),
+ Type = ?config(type, Config),
httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ ?config(port, Config),
+ transport_opts(Type, Config),
+ ?config(node, Config),
http_request(Request, Version, Host),
Expected ++ [{version, Version}]).
http_status(Request, HeadersAndBody, Config, Expected) ->
Version = ?config(http_version, Config),
- Host = ?config(host, Config),
+ Host = ?config(host, Config),
+ Type = ?config(type, Config),
httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ ?config(port, Config),
+ transport_opts(Type, Config),
+ ?config(node, Config),
http_request(Request, Version, Host, HeadersAndBody),
Expected ++ [{version, Version}]).
auth_status(AuthRequest, Config, Expected) ->
Version = ?config(http_version, Config),
Host = ?config(host, Config),
+ Type = ?config(type, Config),
httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ ?config(port, Config),
+ transport_opts(Type, Config),
+ ?config(node, Config),
AuthRequest,
Expected ++ [{version, Version}]).
@@ -772,23 +1537,258 @@ cleanup_mnesia() ->
mnesia:delete_schema([node()]),
ok.
-start_blocker(Config) ->
- spawn(httpd_SUITE, init_blocker, [self(), Config]),
- receive
- blocker_start ->
+transport_opts(ssl, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ [{cacertfile, filename:join(PrivDir, "public_key_cacert.pem")}];
+transport_opts(_, _) ->
+ [].
+
+
+%%% mod_range
+create_range_data(Path) ->
+ PathAndFileName=filename:join([Path,"range.txt"]),
+ case file:read_file(PathAndFileName) of
+ {error, enoent} ->
+ file:write_file(PathAndFileName,list_to_binary(["12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890"]));
+ _ ->
ok
end.
+
+%%% mod_htaccess
+create_htaccess_data(Path, IpAddress)->
+ create_htaccess_dirs(Path),
+
+ create_html_file(filename:join([Path,"ht/open/dummy.html"])),
+ create_html_file(filename:join([Path,"ht/blocknet/dummy.html"])),
+ create_html_file(filename:join([Path,"ht/secret/dummy.html"])),
+ create_html_file(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
-init_blocker(From, Config) ->
- From ! blocker_start,
- block(Config).
+ create_htaccess_file(filename:join([Path,"ht/open/.htaccess"]),
+ Path, "user one Aladdin"),
+ create_htaccess_file(filename:join([Path,"ht/secret/.htaccess"]),
+ Path, "group group1 group2"),
+ create_htaccess_file(filename:join([Path,
+ "ht/secret/top_secret/.htaccess"]),
+ Path, "user four"),
+ create_htaccess_file(filename:join([Path,"ht/blocknet/.htaccess"]),
+ Path, nouser, IpAddress),
+
+ create_user_group_file(filename:join([Path,"ht","users.file"]),
+ "one:OnePassword\ntwo:TwoPassword\nthree:"
+ "ThreePassword\nfour:FourPassword\nAladdin:"
+ "AladdinPassword"),
+ create_user_group_file(filename:join([Path,"ht","groups.file"]),
+ "group1: two one\ngroup2: two three").
+
+create_html_file(PathAndFileName)->
+ file:write_file(PathAndFileName,list_to_binary(
+ "<html><head><title>test</title></head>
+ <body>testar</body></html>")).
+
+create_htaccess_file(PathAndFileName, BaseDir, RequireData)->
+ file:write_file(PathAndFileName,
+ list_to_binary(
+ "AuthUserFile "++ BaseDir ++
+ "/ht/users.file\nAuthGroupFile "++ BaseDir
+ ++ "/ht/groups.file\nAuthName Test\nAuthType"
+ " Basic\n<Limit>\nrequire " ++ RequireData ++
+ "\n</Limit>")).
+
+create_htaccess_file(PathAndFileName, BaseDir, nouser, IpAddress)->
+ file:write_file(PathAndFileName,list_to_binary(
+ "AuthUserFile "++ BaseDir ++
+ "/ht/users.file\nAuthGroupFile " ++
+ BaseDir ++ "/ht/groups.file\nAuthName"
+ " Test\nAuthType"
+ " Basic\n<Limit GET>\n\tallow from " ++
+ format_ip(IpAddress,
+ string:rchr(IpAddress,$.)) ++
+ "\n</Limit>")).
+
+create_user_group_file(PathAndFileName, Data)->
+ file:write_file(PathAndFileName, list_to_binary(Data)).
+
+create_htaccess_dirs(Path)->
+ ok = file:make_dir(filename:join([Path,"ht"])),
+ ok = file:make_dir(filename:join([Path,"ht/open"])),
+ ok = file:make_dir(filename:join([Path,"ht/blocknet"])),
+ ok = file:make_dir(filename:join([Path,"ht/secret"])),
+ ok = file:make_dir(filename:join([Path,"ht/secret/top_secret"])).
+
+remove_htaccess_dirs(Path)->
+ file:del_dir(filename:join([Path,"ht/secret/top_secret"])),
+ file:del_dir(filename:join([Path,"ht/secret"])),
+ file:del_dir(filename:join([Path,"ht/blocknet"])),
+ file:del_dir(filename:join([Path,"ht/open"])),
+ file:del_dir(filename:join([Path,"ht"])).
+
+format_ip(IpAddress,Pos)when Pos > 0->
+ case lists:nth(Pos,IpAddress) of
+ $.->
+ case lists:nth(Pos-2,IpAddress) of
+ $.->
+ format_ip(IpAddress,Pos-3);
+ _->
+ lists:sublist(IpAddress,Pos-2) ++ "."
+ end;
+ _ ->
+ format_ip(IpAddress,Pos-1)
+ end;
+
+format_ip(IpAddress, _Pos)->
+ "1" ++ IpAddress.
+
+remove_htaccess(Path)->
+ file:delete(filename:join([Path,"ht/open/dummy.html"])),
+ file:delete(filename:join([Path,"ht/secret/dummy.html"])),
+ file:delete(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
+ file:delete(filename:join([Path,"ht/blocknet/dummy.html"])),
+ file:delete(filename:join([Path,"ht/blocknet/.htaccess"])),
+ file:delete(filename:join([Path,"ht/open/.htaccess"])),
+ file:delete(filename:join([Path,"ht/secret/.htaccess"])),
+ file:delete(filename:join([Path,"ht/secret/top_secret/.htaccess"])),
+ file:delete(filename:join([Path,"ht","users.file"])),
+ file:delete(filename:join([Path,"ht","groups.file"])),
+ remove_htaccess_dirs(Path).
+
+dos_hostname(Type, Port, Host, Node, Version, Max) ->
+ TooLongHeader = lists:append(lists:duplicate(Max + 1, "a")),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ dos_hostname_request("", Version),
+ [{statuscode, 200},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ dos_hostname_request("dummy-host.ericsson.se", Version),
+ [{statuscode, 200},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ dos_hostname_request(TooLongHeader, Version),
+ [{statuscode, dos_code(Version)},
+ {version, Version}]).
+dos_hostname_request(Host, Version) ->
+ dos_http_request("GET / ", Version, Host).
+
+dos_http_request(Request, "HTTP/1.1" = Version, Host) ->
+ http_request(Request, Version, Host);
+dos_http_request(Request, Version, Host) ->
+ Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n\r\n".
+
+dos_code("HTTP/1.0") ->
+ 403; %% 413 not defined in HTTP/1.0
+dos_code(_) ->
+ 413.
+
+update_password(Node, ServerRoot, _Address, Port, AuthPrefix, Dir, Old, New)->
+ Directory = filename:join([ServerRoot, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, update_password,
+ [undefined, Port, Directory, Old, New, New]).
+
+add_user(Node, Root, Port, AuthPrefix, Dir, User, Password, UserData) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, add_user,
+ [User, Password, UserData, Addr, Port, Directory]).
+
+
+delete_user(Node, Root, _Host, Port, AuthPrefix, Dir, User) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, delete_user, [User, Addr, Port, Directory]).
+remove_users(Node, ServerRoot, Host, Port, AuthPrefix, Dir) ->
+ %% List users, delete them, and make sure they are gone.
+ case list_users(Node, ServerRoot, Host, Port, AuthPrefix, Dir) of
+ {ok, Users} ->
+ lists:foreach(fun(User) ->
+ delete_user(Node, ServerRoot, Host,
+ Port, AuthPrefix, Dir, User)
+ end,
+ Users),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port, AuthPrefix, Dir);
+ _ ->
+ ok
+ end.
+
+list_users(Node, Root, _Host, Port, AuthPrefix, Dir) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, list_users, [Addr, Port, Directory]).
+
+remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, Dir) ->
+ {ok, Groups} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, Dir),
+ lists:foreach(fun(Group) ->
+ delete_group(Node, Group, Port, ServerRoot, AuthPrefix, Dir)
+ end,
+ Groups),
+ {ok, []} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, Dir).
+
+delete_group(Node, Group, Port, Root, AuthPrefix, Dir) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, delete_group, [Group, Addr, Port, Directory]).
+
+list_groups(Node, Root, _, Port, AuthPrefix, Dir) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, list_groups, [Addr, Port, Directory]).
+
+add_group_member(Node, Root, Port, AuthPrefix, Dir, User, Group) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, add_group_member, [Group, User, Addr, Port,
+ Directory]).
+getaddr() ->
+ {ok,HostName} = inet:gethostname(),
+ {ok,{A1,A2,A3,A4}} = inet:getaddr(HostName,inet),
+ lists:flatten(io_lib:format("~p.~p.~p.~p",[A1,A2,A3,A4])).
+
+receive_security_event(Event, Node, Port) ->
+ receive
+ Event ->
+ ok;
+ {'EXIT', _, _} ->
+ receive_security_event(Event, Node, Port)
+ after 5000 ->
+ %% Flush the message queue, to see if we got something...
+ inets_test_lib:flush()
+ end.
+
+list_blocked_users(Node,Port) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, list_blocked_users, [Addr,Port]).
+
+list_blocked_users(Node,Port,Dir) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, list_blocked_users, [Addr,Port,Dir]).
+
+block_user(Node,User,Port,Dir,Sec) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, block_user, [User, Addr, Port, Dir, Sec]).
+
+unblock_user(Node,User,Port,Dir) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, unblock_user, [User, Addr, Port, Dir]).
+
+list_auth_users(Node,Port) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, list_auth_users, [Addr,Port]).
+
+list_auth_users(Node,Port,Dir) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, list_auth_users, [Addr,Port,Dir]).
+
+event(What, Port, Dir, Data) ->
+ Msg = {event, What, Port, Dir, Data},
+ case global:whereis_name(mod_security_test) of
+ undefined ->
+ ok;
+ _Pid ->
+ global:send(mod_security_test, Msg)
+ end.
-block(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("GET /eval?httpd_example:delay(1000) ",
- Version, Host),
- [{statuscode, 200},
- {version, Version}]).
diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index 2d06f3e70c..1fcc5f257e 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,9 +32,9 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [
- uri_too_long_414,
+ [uri_too_long_414,
header_too_long_413,
+ entity_too_long,
erl_script_nocache_opt,
script_nocache,
escaped_url_in_error_body,
@@ -63,14 +63,13 @@ end_per_group(_GroupName, Config) ->
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- tsp("init_per_suite -> entry with"
- "~n Config: ~p", [Config]),
- ok = inets:start(),
+ inets_test_lib:stop_apps([inets]),
+ inets_test_lib:start_apps([inets]),
PrivDir = ?config(priv_dir, Config),
DataDir = ?config(data_dir, Config),
-
+
Dummy =
-"<HTML>
+ "<HTML>
<HEAD>
<TITLE>/index.html</TITLE>
</HEAD>
@@ -78,7 +77,7 @@ init_per_suite(Config) ->
DUMMY
</BODY>
</HTML>",
-
+
DummyFile = filename:join([PrivDir,"dummy.html"]),
CgiDir = filename:join(PrivDir, "cgi-bin"),
ok = file:make_dir(CgiDir),
@@ -115,8 +114,6 @@ DUMMY
%% Description: Cleanup after the whole suite
%%--------------------------------------------------------------------
end_per_suite(_Config) ->
- tsp("end_per_suite -> entry with"
- "~n Config: ~p", [_Config]),
inets:stop(),
ok.
@@ -133,8 +130,6 @@ end_per_suite(_Config) ->
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_testcase(Case, Config) ->
- tsp("init_per_testcase(~w) -> entry with"
- "~n Config: ~p", [Case, Config]),
Config.
@@ -146,22 +141,18 @@ init_per_testcase(Case, Config) ->
%% A list of key/value pairs, holding the test case configuration.
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- tsp("end_per_testcase(~w) -> entry with"
- "~n Config: ~p", [Case, Config]),
+end_per_testcase(_Case, Config) ->
Config.
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-uri_too_long_414(doc) ->
- ["Test that too long uri's get 414 HTTP code"];
-uri_too_long_414(suite) ->
- [];
+uri_too_long_414() ->
+ [{doc, "Test that too long uri's get 414 HTTP code"}].
uri_too_long_414(Config) when is_list(Config) ->
HttpdConf = ?config(httpd_conf, Config),
- {ok, Pid} = inets:start(httpd, [{port, 0}, {max_uri_size, 10}
+ {ok, Pid} = inets:start(httpd, [{max_uri_size, 10}
| HttpdConf]),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
@@ -177,17 +168,12 @@ uri_too_long_414(Config) when is_list(Config) ->
{version, "HTTP/0.9"}]),
inets:stop(httpd, Pid).
-
-%%-------------------------------------------------------------------------
%%-------------------------------------------------------------------------
-
-header_too_long_413(doc) ->
- ["Test that too long headers's get 413 HTTP code"];
-header_too_long_413(suite) ->
- [];
+header_too_long_413() ->
+ [{doc,"Test that too long headers's get 413 HTTP code"}].
header_too_long_413(Config) when is_list(Config) ->
HttpdConf = ?config(httpd_conf, Config),
- {ok, Pid} = inets:start(httpd, [{port, 0}, {max_header_size, 10}
+ {ok, Pid} = inets:start(httpd, [{max_header_size, 10}
| HttpdConf]),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
@@ -201,8 +187,72 @@ header_too_long_413(Config) when is_list(Config) ->
inets:stop(httpd, Pid).
%%-------------------------------------------------------------------------
+
+entity_too_long() ->
+ [{doc, "Test that too long versions and method strings are rejected"}].
+entity_too_long(Config) when is_list(Config) ->
+ HttpdConf = ?config(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, HttpdConf),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ Address = proplists:get_value(bind_address, Info),
+
+ %% Not so long but wrong
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ "GET / " ++
+ lists:duplicate(5, $A) ++ "\r\n\r\n",
+ [{statuscode, 400},
+ %% Server will send lowest version
+ %% as it will not get to the
+ %% client version
+ %% before aborting
+ {version, "HTTP/0.9"}]),
+
+ %% Too long
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ "GET / " ++
+ lists:duplicate(100, $A) ++ "\r\n\r\n",
+ [{statuscode, 413},
+ %% Server will send lowest version
+ %% as it will not get to the
+ %% client version
+ %% before aborting
+ {version, "HTTP/0.9"}]),
+ %% Not so long but wrong
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ lists:duplicate(5, $A) ++ " / "
+ "HTTP/1.1\r\n\r\n",
+ [{statuscode, 501},
+ %% Server will send lowest version
+ %% as it will not get to the
+ %% client version
+ %% before aborting
+ {version, "HTTP/1.1"}]),
+ %% Too long
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ lists:duplicate(100, $A) ++ " / "
+ "HTTP/1.1\r\n\r\n",
+ [{statuscode, 413},
+ %% Server will send lowest version
+ %% as it will not get to the
+ %% client version
+ %% before aborting
+ {version, "HTTP/0.9"}]),
+ inets:stop(httpd, Pid).
+
%%-------------------------------------------------------------------------
+script_nocache() ->
+ [{doc,"Test nocache option for mod_cgi and mod_esi"}].
+script_nocache(Config) when is_list(Config) ->
+ Normal = {no_header, "cache-control"},
+ NoCache = {header, "cache-control", "no-cache"},
+ verify_script_nocache(Config, false, false, Normal, Normal),
+ verify_script_nocache(Config, true, false, NoCache, Normal),
+ verify_script_nocache(Config, false, true, Normal, NoCache),
+ verify_script_nocache(Config, true, true, NoCache, NoCache).
+
+%%-------------------------------------------------------------------------
erl_script_nocache_opt(doc) ->
["Test that too long headers's get 413 HTTP code"];
erl_script_nocache_opt(suite) ->
@@ -224,155 +274,49 @@ erl_script_nocache_opt(Config) when is_list(Config) ->
inets:stop(httpd, Pid).
%%-------------------------------------------------------------------------
-%%-------------------------------------------------------------------------
-script_nocache(doc) ->
- ["Test nocache option for mod_cgi and mod_esi"];
-script_nocache(suite) ->
- [];
-script_nocache(Config) when is_list(Config) ->
- Normal = {no_header, "cache-control"},
- NoCache = {header, "cache-control", "no-cache"},
- verify_script_nocache(Config, false, false, Normal, Normal),
- verify_script_nocache(Config, true, false, NoCache, Normal),
- verify_script_nocache(Config, false, true, Normal, NoCache),
- verify_script_nocache(Config, true, true, NoCache, NoCache),
- ok.
-verify_script_nocache(Config, CgiNoCache, EsiNoCache, CgiOption, EsiOption) ->
- HttpdConf = ?config(httpd_conf, Config),
- CgiScript = ?config(cgi_printenv, Config),
- CgiDir = ?config(cgi_dir, Config),
- {ok, Pid} = inets:start(httpd, [{port, 0},
- {script_alias,
- {"/cgi-bin/", CgiDir ++ "/"}},
- {script_nocache, CgiNoCache},
- {erl_script_alias,
- {"/cgi-bin/erl", [httpd_example,io]}},
- {erl_script_nocache, EsiNoCache}
- | HttpdConf]),
- Info = httpd:info(Pid),
- Port = proplists:get_value(port, Info),
- Address = proplists:get_value(bind_address, Info),
- ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
- "GET /cgi-bin/" ++ CgiScript ++
- " HTTP/1.0\r\n\r\n",
- [{statuscode, 200},
- CgiOption,
- {version, "HTTP/1.0"}]),
- ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
- "GET /cgi-bin/erl/httpd_example:get "
- "HTTP/1.0\r\n\r\n",
- [{statuscode, 200},
- EsiOption,
- {version, "HTTP/1.0"}]),
- inets:stop(httpd, Pid).
-
-
-%%-------------------------------------------------------------------------
%%-------------------------------------------------------------------------
-escaped_url_in_error_body(doc) ->
- ["Test Url-encoding see OTP-8940"];
-escaped_url_in_error_body(suite) ->
- [];
-escaped_url_in_error_body(Config) when is_list(Config) ->
- %% <CONDITIONAL-SKIP>
- %% This skip is due to a problem on windows with long path's
- %% If a path is too long file:open fails with, for example, eio.
- %% Until that problem is fixed, we skip this case...
- Skippable = [win32],
- Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
- tsp("escaped_url_in_error_body -> entry"),
+escaped_url_in_error_body() ->
+ [{doc, "Test Url-encoding see OTP-8940"}].
+escaped_url_in_error_body(Config) when is_list(Config) ->
HttpdConf = ?config(httpd_conf, Config),
{ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
- _Address = proplists:get_value(bind_address, Info),
-
- %% Request 1
- tss(1000),
- tsp("escaped_url_in_error_body -> request 1"),
URL1 = ?URL_START ++ integer_to_list(Port),
- %% Make sure the server is ok, by making a request for a valid page
- case httpc:request(get, {URL1 ++ "/dummy.html", []},
- [{url_encode, false},
- {version, "HTTP/1.0"}],
- [{full_result, false}]) of
- {ok, {200, _}} ->
- %% Don't care about the the body, just that we get a ok response
- ok;
- {ok, {StatusCode1, Body1}} ->
- tsf({unexpected_ok_1, StatusCode1, Body1})
- end,
-
- %% Request 2
- tss(1000),
- tsp("escaped_url_in_error_body -> request 2"),
- %% Make sure the server is ok, by making a request for a valid page
- case httpc:request(get, {URL1 ++ "/dummy.html", []},
- [{url_encode, true},
- {version, "HTTP/1.0"}],
- [{full_result, false}]) of
- {ok, {200, _}} ->
- %% Don't care about the the body, just that we get a ok response
- ok;
- {ok, {StatusCode2, Body2}} ->
- tsf({unexpected_ok_2, StatusCode2, Body2})
- end,
-
- %% Request 3
- tss(1000),
- tsp("escaped_url_in_error_body -> request 3"),
+
+ %% Sanity check
+ {ok, {200, _}} = httpc:request(get, {URL1 ++ "/dummy.html", []},
+ [{url_encode, false},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]),
+ {ok, {200, _}} = httpc:request(get, {URL1 ++ "/dummy.html", []},
+ [{url_encode, true},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]),
+
%% Ask for a non-existing page(1)
Path = "/<b>this_is_bold<b>",
HTMLEncodedPath = http_util:html_encode(Path),
URL2 = URL1 ++ Path,
- case httpc:request(get, {URL2, []},
- [{url_encode, true},
- {version, "HTTP/1.0"}],
- [{full_result, false}]) of
- {ok, {404, Body3}} ->
- case find_URL_path(string:tokens(Body3, " ")) of
- HTMLEncodedPath ->
- ok;
- BadPath3 ->
- tsf({unexpected_path_3, HTMLEncodedPath, BadPath3})
- end;
- {ok, UnexpectedOK3} ->
- tsf({unexpected_ok_3, UnexpectedOK3})
- end,
+ {ok, {404, Body3}} = httpc:request(get, {URL2, []},
+ [{url_encode, true},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]),
- %% Request 4
- tss(1000),
- tsp("escaped_url_in_error_body -> request 4"),
- %% Ask for a non-existing page(2)
- case httpc:request(get, {URL2, []},
- [{url_encode, false},
- {version, "HTTP/1.0"}],
- [{full_result, false}]) of
- {ok, {404, Body4}} ->
- case find_URL_path(string:tokens(Body4, " ")) of
- HTMLEncodedPath ->
- ok;
- BadPath4 ->
- tsf({unexpected_path_4, HTMLEncodedPath, BadPath4})
- end;
- {ok, UnexpectedOK4} ->
- tsf({unexpected_ok_4, UnexpectedOK4})
- end,
- tss(1000),
- tsp("escaped_url_in_error_body -> stop inets"),
- inets:stop(httpd, Pid),
- tsp("escaped_url_in_error_body -> done"),
- ok.
+ HTMLEncodedPath = find_URL_path(string:tokens(Body3, " ")),
+ {ok, {404, Body4}} = httpc:request(get, {URL2, []},
+ [{url_encode, false},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]),
+
+ HTMLEncodedPath = find_URL_path(string:tokens(Body4, " ")),
+ inets:stop(httpd, Pid).
%%-------------------------------------------------------------------------
-%%-------------------------------------------------------------------------
keep_alive_timeout(doc) ->
["Test the keep_alive_timeout option"];
@@ -392,7 +336,6 @@ keep_alive_timeout(Config) when is_list(Config) ->
inets:stop(httpd, Pid).
%%-------------------------------------------------------------------------
-%%-------------------------------------------------------------------------
script_timeout(doc) ->
["Test the httpd script_timeout option"];
@@ -422,12 +365,10 @@ verify_script_timeout(Config, ScriptTimeout, StatusCode) ->
{version, "HTTP/1.0"}]),
inets:stop(httpd, Pid).
-
-%%-------------------------------------------------------------------------
%%-------------------------------------------------------------------------
-slowdose(doc) ->
- ["Testing minimum bytes per second option"];
+slowdose() ->
+ [{doc, "Testing minimum bytes per second option"}].
slowdose(Config) when is_list(Config) ->
HttpdConf = ?config(httpd_conf, Config),
{ok, Pid} = inets:start(httpd, [{port, 0}, {minimum_bytes_per_second, 200}|HttpdConf]),
@@ -438,6 +379,40 @@ slowdose(Config) when is_list(Config) ->
after 6000 ->
{error, closed} = gen_tcp:send(Socket, "Hey")
end.
+
+%%-------------------------------------------------------------------------
+%% Internal functions
+%%-------------------------------------------------------------------------
+
+verify_script_nocache(Config, CgiNoCache, EsiNoCache, CgiOption, EsiOption) ->
+ HttpdConf = ?config(httpd_conf, Config),
+ CgiScript = ?config(cgi_printenv, Config),
+ CgiDir = ?config(cgi_dir, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0},
+ {script_alias,
+ {"/cgi-bin/", CgiDir ++ "/"}},
+ {script_nocache, CgiNoCache},
+ {erl_script_alias,
+ {"/cgi-bin/erl", [httpd_example,io]}},
+ {erl_script_nocache, EsiNoCache}
+ | HttpdConf]),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ Address = proplists:get_value(bind_address, Info),
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ "GET /cgi-bin/" ++ CgiScript ++
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ CgiOption,
+ {version, "HTTP/1.0"}]),
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ "GET /cgi-bin/erl/httpd_example:get "
+ "HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ EsiOption,
+ {version, "HTTP/1.0"}]),
+ inets:stop(httpd, Pid).
+
find_URL_path([]) ->
"";
find_URL_path(["URL", URL | _]) ->
@@ -445,21 +420,6 @@ find_URL_path(["URL", URL | _]) ->
find_URL_path([_ | Rest]) ->
find_URL_path(Rest).
-
-tsp(F) ->
- inets_test_lib:tsp(F).
-tsp(F, A) ->
- inets_test_lib:tsp(F, A).
-
-tsf(Reason) ->
- inets_test_lib:tsf(Reason).
-
-tss(Time) ->
- inets_test_lib:tss(Time).
-
-
-
-
skip(Reason) ->
{skip, Reason}.
diff --git a/lib/inets/test/httpd_block.erl b/lib/inets/test/httpd_block.erl
index 706d014bda..9790623b6f 100644
--- a/lib/inets/test/httpd_block.erl
+++ b/lib/inets/test/httpd_block.erl
@@ -111,8 +111,7 @@ block_disturbing_active_timeout_not_released(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
Poller = long_poll(Type, Host, Port, Node, 200, 60000),
ct:sleep(15000),
- Blocker = blocker(Node, Host, Port, 50000),
- await_normal_process_exit(Blocker, "blocker", 50000),
+ ok = httpd_block(undefined, Port, disturbing, 50000),
await_normal_process_exit(Poller, "poller", 30000),
blocked = get_admin_state(Node, Host, Port),
process_flag(trap_exit, false),
@@ -123,8 +122,7 @@ block_disturbing_active_timeout_released(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
Poller = long_poll(Type, Host, Port, Node, 200, 40000),
ct:sleep(5000),
- Blocker = blocker(Node, Host, Port, 10000),
- await_normal_process_exit(Blocker, "blocker", 15000),
+ ok = httpd_block(undefined, Port, disturbing, 10000),
await_suite_failed_process_exit(Poller, "poller", 40000,
connection_closed),
blocked = get_admin_state(Node, Host, Port),
diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl
index 6406eeae79..36a5bb9e71 100644
--- a/lib/inets/test/httpd_test_lib.erl
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -92,16 +92,6 @@ verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut)
verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut).
verify_request(SocketType, Host, Port, TranspOpts0, Node, RequestStr, Options, TimeOut) ->
- tsp("verify_request -> entry with"
- "~n SocketType: ~p"
- "~n Host: ~p"
- "~n Port: ~p"
- "~n TranspOpts: ~p"
- "~n Node: ~p"
- "~n Options: ~p"
- "~n TimeOut: ~p",
- [SocketType, Host, Port, TranspOpts0, Node, Options, TimeOut]),
-
%% For now, until we modernize the httpd tests
TranspOpts =
case lists:member(inet6, TranspOpts0) of
@@ -113,10 +103,7 @@ verify_request(SocketType, Host, Port, TranspOpts0, Node, RequestStr, Options, T
try inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts) of
{ok, Socket} ->
- tsp("verify_request -> connected - now send message"),
- SendRes = inets_test_lib:send(SocketType, Socket, RequestStr),
- tsp("verify_request -> send result: "
- "~n ~p", [SendRes]),
+ ok = inets_test_lib:send(SocketType, Socket, RequestStr),
State = case inets_regexp:match(RequestStr, "printenv") of
nomatch ->
#state{};
@@ -127,37 +114,24 @@ verify_request(SocketType, Host, Port, TranspOpts0, Node, RequestStr, Options, T
case request(State#state{request = RequestStr,
socket = Socket}, TimeOut) of
{error, Reason} ->
- tsp("verify_request -> request failed: "
- "~n Reason: ~p", [Reason]),
{error, Reason};
NewState ->
- tsp("verify_request -> validate reply: "
- "~n NewState: ~p", [NewState]),
ValidateResult =
validate(RequestStr, NewState, Options, Node, Port),
- tsp("verify_request -> validation result: "
- "~n ~p", [ValidateResult]),
inets_test_lib:close(SocketType, Socket),
ValidateResult
end;
ConnectError ->
- tsp("verify_request -> connect error: "
- "~n ~p"
- "~n", [ConnectError]),
- tsf({connect_error, ConnectError,
- [SocketType, Host, Port, TranspOpts]})
+ ct:fail({connect_error, ConnectError,
+ [SocketType, Host, Port, TranspOpts]})
catch
T:E ->
- tsp("verify_request -> connect failed: "
- "~n E: ~p"
- "~n T: ~p"
- "~n", [E, T]),
- tsf({connect_failure,
- [{type, T},
- {error, E},
- {stacktrace, erlang:get_stacktrace()},
- {args, [SocketType, Host, Port, TranspOpts]}]})
+ ct:fail({connect_failure,
+ [{type, T},
+ {error, E},
+ {stacktrace, erlang:get_stacktrace()},
+ {args, [SocketType, Host, Port, TranspOpts]}]})
end.
request(#state{mfa = {Module, Function, Args},
@@ -166,10 +140,6 @@ request(#state{mfa = {Module, Function, Args},
HeadRequest = lists:sublist(RequestStr, 1, 4),
receive
{tcp, Socket, Data} ->
- io:format("~p ~w[~w]request -> received (tcp) data"
- "~n Data: ~p"
- "~n", [self(), ?MODULE, ?LINE, Data]),
- print(tcp, Data, State),
case Module:Function([Data | Args]) of
{ok, Parsed} ->
handle_http_msg(Parsed, State);
@@ -179,22 +149,12 @@ request(#state{mfa = {Module, Function, Args},
request(State#state{mfa = NewMFA}, TimeOut)
end;
{tcp_closed, Socket} when Function =:= whole_body ->
- io:format("~p ~w[~w]request -> "
- "received (tcp) closed when whole_body"
- "~n", [self(), ?MODULE, ?LINE]),
- print(tcp, "closed", State),
State#state{body = hd(Args)};
{tcp_closed, Socket} ->
- io:format("~p ~w[~w]request -> received (tcp) closed"
- "~n", [self(), ?MODULE, ?LINE]),
exit({test_failed, connection_closed});
{tcp_error, Socket, Reason} ->
- io:format("~p ~w[~w]request -> received (tcp) error"
- "~n Reason: ~p"
- "~n", [self(), ?MODULE, ?LINE, Reason]),
ct:fail({tcp_error, Reason});
{ssl, Socket, Data} ->
- print(ssl, Data, State),
case Module:Function([Data | Args]) of
{ok, Parsed} ->
handle_http_msg(Parsed, State);
@@ -204,28 +164,19 @@ request(#state{mfa = {Module, Function, Args},
request(State#state{mfa = NewMFA}, TimeOut)
end;
{ssl_closed, Socket} when Function =:= whole_body ->
- print(ssl, "closed", State),
State#state{body = hd(Args)};
{ssl_closed, Socket} ->
exit({test_failed, connection_closed});
{ssl_error, Socket, Reason} ->
ct:fail({ssl_error, Reason})
after TimeOut ->
- io:format("~p ~w[~w]request -> timeout"
- "~n", [self(), ?MODULE, ?LINE]),
+ ct:pal("~p ~w[~w]request -> timeout"
+ "~n", [self(), ?MODULE, ?LINE]),
ct:fail(connection_timed_out)
end.
handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body},
State = #state{request = RequestStr}) ->
- io:format("~p ~w[~w]handle_http_msg -> entry with"
- "~n Version: ~p"
- "~n StatusCode: ~p"
- "~n ReasonPharse: ~p"
- "~n Headers: ~p"
- "~n Body: ~p"
- "~n", [self(), ?MODULE, ?LINE,
- Version, StatusCode, ReasonPharse, Headers, Body]),
case is_expect(RequestStr) of
true ->
State#state{status_line = {Version,
@@ -285,11 +236,6 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _},
headers = Headers,
body = Body}, Options, N, P) ->
- tsp("validate -> entry with"
- "~n StatusCode: ~p"
- "~n Headers: ~p"
- "~n Body: ~p", [StatusCode, Headers, Body]),
-
check_version(Version, Options),
case lists:keysearch(statuscode, 1, Options) of
{value, _} ->
@@ -311,20 +257,20 @@ check_version(Version, Options) ->
{value, {version, Version}} ->
ok;
{value, {version, Ver}} ->
- tsf({wrong_version, [{got, Version},
- {expected, Ver}]});
+ ct:fail({wrong_version, [{got, Version},
+ {expected, Ver}]});
_ ->
- case Version of
- "HTTP/1.1" ->
- ok;
+ case Version of
+ "HTTP/1.1" ->
+ ok;
_ ->
- tsf({wrong_version, [{got, Version},
- {expected, "HTTP/1.1"}]})
- end
+ ct:fail({wrong_version, [{got, Version},
+ {expected, "HTTP/1.1"}]})
+ end
end.
check_status_code(StatusCode, [], Options) ->
- tsf({wrong_status_code, [{got, StatusCode}, {expected, Options}]});
+ ct:fail({wrong_status_code, [{got, StatusCode}, {expected, Options}]});
check_status_code(StatusCode, Current = [_ | Rest], Options) ->
case lists:keysearch(statuscode, 1, Current) of
{value, {statuscode, StatusCode}} ->
@@ -332,7 +278,7 @@ check_status_code(StatusCode, Current = [_ | Rest], Options) ->
{value, {statuscode, _OtherStatus}} ->
check_status_code(StatusCode, Rest, Options);
false ->
- tsf({wrong_status_code, [{got, StatusCode}, {expected, Options}]})
+ ct:fail({wrong_status_code, [{got, StatusCode}, {expected, Options}]})
end.
do_validate(_, [], _, _) ->
@@ -345,9 +291,9 @@ do_validate(Header, [{header, HeaderField}|Rest], N, P) ->
{value, {LowerHeaderField, _Value}} ->
ok;
false ->
- tsf({missing_header_field, LowerHeaderField, Header});
+ ct:fail({missing_header_field, LowerHeaderField, Header});
_ ->
- tsf({missing_header_field, LowerHeaderField, Header})
+ ct:fail({missing_header_field, LowerHeaderField, Header})
end,
do_validate(Header, Rest, N, P);
do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) ->
@@ -356,15 +302,15 @@ do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) ->
{value, {LowerHeaderField, Value}} ->
ok;
false ->
- tsf({wrong_header_field_value, LowerHeaderField, Header});
+ ct:fail({wrong_header_field_value, LowerHeaderField, Header});
_ ->
- tsf({wrong_header_field_value, LowerHeaderField, Header})
+ ct:fail({wrong_header_field_value, LowerHeaderField, Header})
end,
do_validate(Header, Rest, N, P);
do_validate(Header,[{no_header, HeaderField}|Rest],N,P) ->
case lists:keysearch(HeaderField,1,Header) of
{value,_} ->
- tsf({wrong_header_field_value, HeaderField, Header});
+ ct:fail({wrong_header_field_value, HeaderField, Header});
_ ->
ok
end,
@@ -382,14 +328,14 @@ is_expect(RequestStr) ->
%% OTP-5775, content-length
check_body("GET /cgi-bin/erl/httpd_example:get_bin HTTP/1.0\r\n\r\n", 200, "text/html", Length, _Body) when (Length =/= 274) ->
- tsf(content_length_error);
+ ct:fail(content_length_error);
check_body("GET /cgi-bin/cgi_echo HTTP/1.0\r\n\r\n", 200, "text/plain",
_, Body) ->
case size(Body) of
100 ->
ok;
_ ->
- tsf(content_length_error)
+ ct:fail(content_length_error)
end;
check_body(RequestStr, 200, "text/html", _, Body) ->
@@ -404,16 +350,3 @@ check_body(RequestStr, 200, "text/html", _, Body) ->
check_body(_, _, _, _,_) ->
ok.
-print(Proto, Data, #state{print = true}) ->
- ct:pal("Received ~p: ~p~n", [Proto, Data]);
-print(_, _, #state{print = false}) ->
- ok.
-
-
-tsp(F) ->
- inets_test_lib:tsp(F).
-tsp(F, A) ->
- inets_test_lib:tsp(F, A).
-
-tsf(Reason) ->
- inets_test_lib:tsf(Reason).
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 12b85a816f..60979278fc 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -77,75 +77,32 @@ end_per_suite(_) ->
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_testcase(httpd_subtree, Config) ->
- io:format("init_per_testcase(httpd_subtree) -> entry with"
- "~n Config: ~p"
- "~n", [Config]),
Dog = test_server:timetrap(?t:minutes(1)),
NewConfig = lists:keydelete(watchdog, 1, Config),
-
- DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
- ServerROOT = filename:join(PrivDir, "server_root"),
- DocROOT = filename:join(PrivDir, "htdocs"),
- ConfDir = filename:join(ServerROOT, "conf"),
-
- io:format("init_per_testcase(httpd_subtree) -> create dir(s)"
- "~n", []),
- file:make_dir(ServerROOT), %% until http_test is cleaned up!
- ok = file:make_dir(DocROOT),
- ok = file:make_dir(ConfDir),
-
- io:format("init_per_testcase(httpd_subtree) -> copy file(s)"
- "~n", []),
- {ok, _} = inets_test_lib:copy_file("simple.conf", DataDir, PrivDir),
- {ok, _} = inets_test_lib:copy_file("mime.types", DataDir, ConfDir),
-
- io:format("init_per_testcase(httpd_subtree) -> write file(s)"
- "~n", []),
- ConfFile = filename:join(PrivDir, "simple.conf"),
- {ok, Fd} = file:open(ConfFile, [append]),
- ok = file:write(Fd, "ServerRoot " ++ ServerROOT ++ "\n"),
- ok = file:write(Fd, "DocumentRoot " ++ DocROOT ++ "\n"),
- ok = file:close(Fd),
-
- %% To make sure application:set_env is not overwritten by any
- %% app-file settings.
- io:format("init_per_testcase(httpd_subtree) -> load inets app"
- "~n", []),
- application:load(inets),
- io:format("init_per_testcase(httpd_subtree) -> update inets env"
- "~n", []),
- ok = application:set_env(inets, services, [{httpd, ConfFile}]),
-
+
+ SimpleConfig = [{port, 0},
+ {server_name,"www.test"},
+ {modules, [mod_get]},
+ {server_root, PrivDir},
+ {document_root, PrivDir},
+ {bind_address, any},
+ {ipfamily, inet}],
try
- io:format("init_per_testcase(httpd_subtree) -> start inets app"
- "~n", []),
- ok = inets:start(),
- io:format("init_per_testcase(httpd_subtree) -> done"
- "~n", []),
- [{watchdog, Dog}, {server_root, ServerROOT}, {doc_root, DocROOT},
- {conf_dir, ConfDir}| NewConfig]
+ inets:start(),
+ inets:start(httpd, SimpleConfig),
+ [{watchdog, Dog} | NewConfig]
catch
_:Reason ->
- io:format("init_per_testcase(httpd_subtree) -> "
- "failed starting inets - cleanup"
- "~n Reason: ~p"
- "~n", [Reason]),
- application:unset_env(inets, services),
- application:unload(inets),
+ inets:stop(),
exit({failed_starting_inets, Reason})
end;
-init_per_testcase(Case, Config) ->
- io:format("init_per_testcase(~p) -> entry with"
- "~n Config: ~p"
- "~n", [Case, Config]),
+init_per_testcase(_Case, Config) ->
Dog = test_server:timetrap(?t:minutes(5)),
NewConfig = lists:keydelete(watchdog, 1, Config),
- Stop = inets:stop(),
- io:format("init_per_testcase(~p) -> Stop: ~p"
- "~n", [Case, Stop]),
+ inets:stop(),
ok = inets:start(),
[{watchdog, Dog} | NewConfig].
@@ -260,10 +217,10 @@ tftpd_worker(suite) ->
[];
tftpd_worker(Config) when is_list(Config) ->
[] = supervisor:which_children(tftp_sup),
- {ok, Pid0} = inets:start(tftpd, [{host, "localhost"},
- {port, inet_port()}]),
- {ok, _Pid1} = inets:start(tftpd, [{host, "localhost"},
- {port, inet_port()}], stand_alone),
+ {ok, Pid0} = inets:start(tftpd, [{host, inets_test_lib:hostname()},
+ {port, 0}]),
+ {ok, _Pid1} = inets:start(tftpd, [{host, inets_test_lib:hostname()},
+ {port, 0}], stand_alone),
[{_,Pid0, worker, _}] = supervisor:which_children(tftp_sup),
inets:stop(tftpd, Pid0),
@@ -280,30 +237,21 @@ httpd_subtree(doc) ->
httpd_subtree(suite) ->
[];
httpd_subtree(Config) when is_list(Config) ->
- io:format("httpd_subtree -> entry with"
- "~n Config: ~p"
- "~n", [Config]),
-
%% Check that we have the httpd top supervisor
- io:format("httpd_subtree -> verify inets~n", []),
{ok, _} = verify_child(inets_sup, httpd_sup, supervisor),
%% Check that we have the httpd instance supervisor
- io:format("httpd_subtree -> verify httpd~n", []),
{ok, Id} = verify_child(httpd_sup, httpd_instance_sup, supervisor),
{httpd_instance_sup, Addr, Port} = Id,
Instance = httpd_util:make_name("httpd_instance_sup", Addr, Port),
%% Check that we have the expected httpd instance children
- io:format("httpd_subtree -> verify httpd instance children "
- "(acceptor, misc and manager)~n", []),
{ok, _} = verify_child(Instance, httpd_connection_sup, supervisor),
{ok, _} = verify_child(Instance, httpd_acceptor_sup, supervisor),
{ok, _} = verify_child(Instance, httpd_misc_sup, supervisor),
{ok, _} = verify_child(Instance, httpd_manager, worker),
%% Check that the httpd instance acc supervisor has children
- io:format("httpd_subtree -> verify acc~n", []),
InstanceAcc = httpd_util:make_name("httpd_acceptor_sup", Addr, Port),
case supervisor:which_children(InstanceAcc) of
[_ | _] ->
@@ -328,15 +276,7 @@ httpd_subtree(Config) when is_list(Config) ->
verify_child(Parent, Child, Type) ->
-%% io:format("verify_child -> entry with"
-%% "~n Parent: ~p"
-%% "~n Child: ~p"
-%% "~n Type: ~p"
-%% "~n", [Parent, Child, Type]),
Children = supervisor:which_children(Parent),
-%% io:format("verify_child -> which children"
-%% "~n Children: ~p"
-%% "~n", [Children]),
verify_child(Children, Parent, Child, Type).
verify_child([], Parent, Child, _Type) ->
@@ -344,21 +284,12 @@ verify_child([], Parent, Child, _Type) ->
verify_child([{Id, _Pid, Type2, Mods}|Children], Parent, Child, Type) ->
case lists:member(Child, Mods) of
true when (Type2 =:= Type) ->
-%% io:format("verify_child -> found with expected type"
-%% "~n Id: ~p"
-%% "~n", [Id]),
{ok, Id};
true when (Type2 =/= Type) ->
-%% io:format("verify_child -> found with unexpected type"
-%% "~n Type2: ~p"
-%% "~n Id: ~p"
-%% "~n", [Type2, Id]),
{error, {wrong_type, Type2, Child, Parent}};
false ->
verify_child(Children, Parent, Child, Type)
end.
-
-
%%-------------------------------------------------------------------------
%% httpc_subtree
@@ -368,47 +299,19 @@ httpc_subtree(doc) ->
httpc_subtree(suite) ->
[];
httpc_subtree(Config) when is_list(Config) ->
- tsp("httpc_subtree -> entry with"
- "~n Config: ~p", [Config]),
-
- tsp("httpc_subtree -> start inets service httpc with profile foo"),
- {ok, _Foo} = inets:start(httpc, [{profile, foo}]),
+ {ok, Foo} = inets:start(httpc, [{profile, foo}]),
- tsp("httpc_subtree -> "
- "start stand-alone inets service httpc with profile bar"),
- {ok, _Bar} = inets:start(httpc, [{profile, bar}], stand_alone),
+ {ok, Bar} = inets:start(httpc, [{profile, bar}], stand_alone),
- tsp("httpc_subtree -> retreive list of httpc instances"),
HttpcChildren = supervisor:which_children(httpc_profile_sup),
- tsp("httpc_subtree -> HttpcChildren: ~n~p", [HttpcChildren]),
-
- tsp("httpc_subtree -> verify httpc stand-alone instances"),
+
{value, {httpc_manager, _, worker, [httpc_manager]}} =
lists:keysearch(httpc_manager, 1, HttpcChildren),
- tsp("httpc_subtree -> verify httpc (named) instances"),
- {value,{{httpc,foo}, Pid, worker, [httpc_manager]}} =
+ {value,{{httpc,foo}, _Pid, worker, [httpc_manager]}} =
lists:keysearch({httpc, foo}, 1, HttpcChildren),
false = lists:keysearch({httpc, bar}, 1, HttpcChildren),
- tsp("httpc_subtree -> stop inets"),
- inets:stop(httpc, Pid),
-
- tsp("httpc_subtree -> done"),
- ok.
-
-inet_port() ->
- {ok, Socket} = gen_tcp:listen(0, [{reuseaddr, true}]),
- {ok, Port} = inet:port(Socket),
- gen_tcp:close(Socket),
- Port.
-
-
-tsp(F) ->
- tsp(F, []).
-tsp(F, A) ->
- test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]).
-
-tsf(Reason) ->
- test_server:fail(Reason).
+ inets:stop(httpc, Foo),
+ exit(Bar, normal).
diff --git a/lib/inets/test/inets_sup_SUITE_data/mime.types b/lib/inets/test/inets_sup_SUITE_data/mime.types
deleted file mode 100644
index e52d345ff7..0000000000
--- a/lib/inets/test/inets_sup_SUITE_data/mime.types
+++ /dev/null
@@ -1,3 +0,0 @@
-# MIME type Extension
-text/html html htm
-text/plain asc txt
diff --git a/lib/inets/test/inets_sup_SUITE_data/simple.conf b/lib/inets/test/inets_sup_SUITE_data/simple.conf
deleted file mode 100644
index e1429b4a28..0000000000
--- a/lib/inets/test/inets_sup_SUITE_data/simple.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Port 8888
-ServerName www.test
-SocketType ip_comm
-Modules mod_get
-ServerAdmin [email protected]
-
diff --git a/lib/inets/test/old_httpd_SUITE.erl b/lib/inets/test/old_httpd_SUITE.erl
index de9aa4562e..19c2bc129e 100644
--- a/lib/inets/test/old_httpd_SUITE.erl
+++ b/lib/inets/test/old_httpd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -155,65 +155,105 @@ all() ->
[
{group, ip},
{group, ssl},
- {group, http_1_1_ip},
- {group, http_1_0_ip},
- {group, http_0_9_ip},
- {group, ipv6},
+ %%{group, http_1_1_ip},
+ %%{group, http_1_0_ip},
+ %%{group, http_0_9_ip},
+ %%{group, ipv6},
{group, tickets}
].
groups() ->
[
{ip, [],
- [ip_mod_alias, ip_mod_actions, ip_mod_security,
- ip_mod_auth, ip_mod_auth_api, ip_mod_auth_mnesia_api,
- ip_mod_htaccess, ip_mod_cgi, ip_mod_esi, ip_mod_get,
- ip_mod_head, ip_mod_all, ip_load_light, ip_load_medium,
- ip_load_heavy, ip_dos_hostname, ip_time_test,
- ip_restart_no_block, ip_restart_disturbing_block,
+ [
+ %%ip_mod_alias,
+ ip_mod_actions,
+ %%ip_mod_security,
+ %% ip_mod_auth,
+ %% ip_mod_auth_api,
+ ip_mod_auth_mnesia_api,
+ %%ip_mod_htaccess,
+ %%ip_mod_cgi,
+ %%ip_mod_esi,
+ %%ip_mod_get,
+ %%ip_mod_head,
+ %%ip_mod_all,
+ %% ip_load_light,
+ %% ip_load_medium,
+ %% ip_load_heavy,
+ %%ip_dos_hostname,
+ ip_time_test,
+ %% Only used through load_config
+ %% but we still need these tests
+ %% should be cleaned up and moved to new test suite
+ ip_restart_no_block,
+ ip_restart_disturbing_block,
ip_restart_non_disturbing_block,
- ip_block_disturbing_idle, ip_block_non_disturbing_idle,
- ip_block_503, ip_block_disturbing_active,
+ ip_block_disturbing_idle,
+ ip_block_non_disturbing_idle,
+ ip_block_503,
+ ip_block_disturbing_active,
ip_block_non_disturbing_active,
ip_block_disturbing_active_timeout_not_released,
ip_block_disturbing_active_timeout_released,
ip_block_non_disturbing_active_timeout_not_released,
ip_block_non_disturbing_active_timeout_released,
ip_block_disturbing_blocker_dies,
- ip_block_non_disturbing_blocker_dies]},
+ ip_block_non_disturbing_blocker_dies
+ ]},
{ssl, [], [{group, essl}]},
{essl, [],
- [essl_mod_alias, essl_mod_actions, essl_mod_security,
- essl_mod_auth, essl_mod_auth_api,
- essl_mod_auth_mnesia_api, essl_mod_htaccess,
- essl_mod_cgi, essl_mod_esi, essl_mod_get, essl_mod_head,
- essl_mod_all, essl_load_light, essl_load_medium,
- essl_load_heavy, essl_dos_hostname, essl_time_test,
- essl_restart_no_block, essl_restart_disturbing_block,
- essl_restart_non_disturbing_block,
- essl_block_disturbing_idle,
- essl_block_non_disturbing_idle, essl_block_503,
- essl_block_disturbing_active,
- essl_block_non_disturbing_active,
- essl_block_disturbing_active_timeout_not_released,
- essl_block_disturbing_active_timeout_released,
- essl_block_non_disturbing_active_timeout_not_released,
- essl_block_non_disturbing_active_timeout_released,
- essl_block_disturbing_blocker_dies,
- essl_block_non_disturbing_blocker_dies]},
- {http_1_1_ip, [],
- [ip_host, ip_chunked, ip_expect, ip_range, ip_if_test,
- ip_http_trace, ip_http1_1_head,
- ip_mod_cgi_chunked_encoding_test]},
- {http_1_0_ip, [],
- [ip_head_1_0, ip_get_1_0, ip_post_1_0]},
- {http_0_9_ip, [], [ip_get_0_9]},
- {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm,
- ipv6_hostname_essl, ipv6_address_essl]},
+ [
+ %%essl_mod_alias,
+ essl_mod_actions,
+ %% essl_mod_security,
+ %% essl_mod_auth,
+ %% essl_mod_auth_api,
+ essl_mod_auth_mnesia_api,
+ %%essl_mod_htaccess,
+ %%essl_mod_cgi,
+ %%essl_mod_esi,
+ %%essl_mod_get,
+ %%essl_mod_head,
+ %% essl_mod_all,
+ %% essl_load_light,
+ %% essl_load_medium,
+ %% essl_load_heavy,
+ %%essl_dos_hostname,
+ essl_time_test
+ %% Replaced by load_config
+ %% essl_restart_no_block,
+ %% essl_restart_disturbing_block,
+ %% essl_restart_non_disturbing_block,
+ %% essl_block_disturbing_idle,
+ %% essl_block_non_disturbing_idle, essl_block_503,
+ %% essl_block_disturbing_active,
+ %% essl_block_non_disturbing_active,
+ %% essl_block_disturbing_active_timeout_not_released,
+ %% essl_block_disturbing_active_timeout_released,
+ %% essl_block_non_disturbing_active_timeout_not_released,
+ %% essl_block_non_disturbing_active_timeout_released,
+ %% essl_block_disturbing_blocker_dies,
+ %% essl_block_non_disturbing_blocker_dies
+ ]},
+ %% {http_1_1_ip, [],
+ %% [
+ %% %%ip_host, ip_chunked, ip_expect,
+ %% %%ip_range,
+ %% %%ip_if_test
+ %% %%ip_http_trace, ip_http1_1_head,
+ %% %%ip_mod_cgi_chunked_encoding_test
+ %% ]},
+ %%{http_1_0_ip, [],
+ %%[ip_head_1_0, ip_get_1_0, ip_post_1_0]},
+ %%{http_0_9_ip, [], [ip_get_0_9]},
+ %% {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm,
+ %% ipv6_hostname_essl, ipv6_address_essl]},
{tickets, [],
- [ticket_5775, ticket_5865, ticket_5913, ticket_6003,
- ticket_7304]}].
-
+ [%%ticket_5775, ticket_5865,
+ ticket_5913%%, ticket_6003,
+ %%ticket_7304
+ ]}].
init_per_group(ipv6 = _GroupName, Config) ->
case inets_test_lib:has_ipv6_support() of
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 6b1f149cc8..bbd86c3eb3 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 5.9.8
+INETS_VSN = 5.10.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index 8c45d187bc..e81a9f82d2 100644
--- a/lib/jinterface/doc/src/notes.xml
+++ b/lib/jinterface/doc/src/notes.xml
@@ -30,6 +30,25 @@
</header>
<p>This document describes the changes made to the Jinterface application.</p>
+<section><title>Jinterface 1.5.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Implement support for Maps</p>
+ <p>
+ The API and implementation are simplistic, like for lists
+ and tuples, using arrays and without any connection to
+ java.util.Map. (Thanks to Vlad Dumitrescu)</p>
+ <p>
+ Own Id: OTP-11703</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Jinterface 1.5.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
index 968f284bff..3ef44b8851 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -92,6 +92,7 @@ public class AbstractNode {
static final int dFlagNewFloats = 0x800;
static final int dFlagUnicodeIo = 0x1000;
static final int dFlagUtf8Atoms = 0x10000;
+ static final int dFlagMapTag = 0x20000;
int ntype = NTYPE_R6;
int proto = 0; // tcp/ip
@@ -100,7 +101,7 @@ public class AbstractNode {
int creation = 0;
int flags = dFlagExtendedReferences | dFlagExtendedPidsPorts
| dFlagBitBinaries | dFlagNewFloats | dFlagFunTags
- | dflagNewFunTags | dFlagUtf8Atoms;
+ | dflagNewFunTags | dFlagUtf8Atoms | dFlagMapTag;
/* initialize hostname and default cookie */
static {
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
index 7c1cf84e98..03c18e55a2 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
@@ -125,8 +125,6 @@ public class OtpErlangMap extends OtpErlangObject implements Serializable,
for (int i = 0; i < arity; i++) {
keys[i] = buf.read_any();
- }
- for (int i = 0; i < arity; i++) {
values[i] = buf.read_any();
}
} else {
@@ -227,8 +225,6 @@ public class OtpErlangMap extends OtpErlangObject implements Serializable,
for (int i = 0; i < arity; i++) {
buf.write_any(keys[i]);
- }
- for (int i = 0; i < arity; i++) {
buf.write_any(values[i]);
}
}
diff --git a/lib/jinterface/test/jinterface_SUITE_data/Maps.java b/lib/jinterface/test/jinterface_SUITE_data/Maps.java
index 136a665f23..653defc621 100644
--- a/lib/jinterface/test/jinterface_SUITE_data/Maps.java
+++ b/lib/jinterface/test/jinterface_SUITE_data/Maps.java
@@ -42,16 +42,16 @@ class Maps {
runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 1, 100, 0, 1, 97, 100,
0, 1, 98 }, "#{a => b}", 2);
// make sure keys are sorted here, jinterface doesn't reorder them
- runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 2, 97, 2, 100, 0, 1, 97,
- 106, 97, 1 }, "#{2 => [],a => 1}", 3);
+ runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 2, 97, 2, 106,
+ 100, 0, 1, 97, 97, 1 }, "#{2 => [],a => 1}", 3);
runTest(new byte[] { (byte) 131, 116, 0, 0, 0, 1, 104, 1, 97, 3, 108,
0, 0, 0, 1, 100, 0, 1, 114, 106 }, "#{{3} => [r]}", 4);
try {
// #{2 => [],a => 1}
final OtpErlangMap map = new OtpErlangMap(new OtpInputStream(
- new byte[] { (byte) 131, 116, 0, 0, 0, 2, 97, 2, 100, 0, 1,
- 97, 106, 97, 1 }));
+ new byte[] { (byte) 131, 116, 0, 0, 0, 2, 97, 2, 106,
+ 100, 0, 1, 97, 97, 1 }));
if (map.arity() != 2) {
fail(5);
diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk
index 1954040c3d..c50200fab6 100644
--- a/lib/jinterface/vsn.mk
+++ b/lib/jinterface/vsn.mk
@@ -1 +1 @@
-JINTERFACE_VSN = 1.5.8
+JINTERFACE_VSN = 1.5.9
diff --git a/lib/kernel/doc/src/app.xml b/lib/kernel/doc/src/app.xml
index 7c9d6eecec..8575d94048 100644
--- a/lib/kernel/doc/src/app.xml
+++ b/lib/kernel/doc/src/app.xml
@@ -61,7 +61,8 @@
{applications, Apps},
{env, Env},
{mod, Start},
- {start_phases, Phases}]}.
+ {start_phases, Phases},
+ {runtime_dependencies, RTDeps}]}.
Value Default
----- -------
@@ -77,8 +78,10 @@ Apps [App] []
Env [{Par,Val}] []
Start {Module,StartArgs} []
Phases [{Phase,PhaseArgs}] undefined
+RTDeps [ApplicationVersion] []
Module = Name = App = Par = Phase = atom()
- Val = StartArgs = PhaseArgs = term()</code>
+ Val = StartArgs = PhaseArgs = term()
+ ApplicationVersion = string()</code>
<p><c>Application</c> is the name of the application.</p>
<p>For the application controller, all keys are optional.
The respective default values are used for any omitted keys.</p>
@@ -87,6 +90,8 @@ Phases [{Phase,PhaseArgs}] undefined
<c>description</c>, <c>vsn</c>, <c>modules</c>, <c>registered</c>
and <c>applications</c>. The other keys are ignored by
<c>systools</c>.</p>
+ <warning><p>The <c>RTDeps</c> type was introduced in OTP 17.0 and
+ might be subject to changes during the OTP 17 release.</p></warning>
<taglist>
<tag><c>description</c></tag>
<item>
@@ -185,6 +190,33 @@ Phases [{Phase,PhaseArgs}] undefined
start phases must be a subset of the set of phases defined
for the primary application. Refer to <em>OTP Design Principles</em> for more information.</p>
</item>
+ <tag><marker id="runtime_dependencies"><c>runtime_dependencies</c></marker></tag>
+ <item><p>A list of application versions that the application
+ depends on. An example of such an application version is
+ <c>"kernel-3.0"</c>. Application versions specified as runtime
+ dependencies are minimum requirements. That is, a larger
+ application version than the one specified in the
+ dependency satisfies the requirement. For information on
+ how to compare application versions see
+ <seealso marker="doc/system_principles:versions">the
+ documentation of versions in the system principles
+ guide</seealso>. Note that that the application version
+ specifies a source code version. An additional indirect
+ requirement is that installed binary application of
+ the specified version has been built so that it is
+ compatible with the rest of the system.</p>
+ <p>Some dependencies might only be required in specific runtime
+ scenarios. In the case such optional dependencies exist, these are
+ specified and documented in the corresponding "App" documentation
+ of the specific application.</p>
+ <warning><p>The <c>runtime_dependencies</c> key was introduced in
+ OTP 17.0. The type of its value might be subject to changes during
+ the OTP 17 release.</p></warning>
+ <warning><p>All runtime dependencies specified in OTP applications
+ during the OTP 17 release may not be completely correct. This
+ is actively being worked on. Declared runtime dependencies in OTP
+ applications are expected to be correct in OTP 18.</p></warning>
+ </item>
</taglist>
</section>
diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml
index 016151891c..7664fda4db 100644
--- a/lib/kernel/doc/src/application.xml
+++ b/lib/kernel/doc/src/application.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -459,8 +459,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
<name>Module:start(StartType, StartArgs) -> {ok, Pid} | {ok, Pid, State} | {error, Reason}</name>
<fsummary>Start an application</fsummary>
<type>
- <v>StartType = normal | {takeover,Node} | {failover,Node}</v>
- <v>&nbsp;Node = node()</v>
+ <v>StartType = <seealso marker="#type-start_type">start_type()</seealso></v>
<v>StartArgs = term()</v>
<v>Pid = pid()</v>
<v>State = term()</v>
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index b3ec9fd33d..8dae34431b 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -642,6 +642,11 @@
<item>
<p>Symbolic links are not supported on this platform.</p>
</item>
+ <tag><c>eperm</c></tag>
+ <item>
+ <p>User does not have privileges to create symbolic links
+ (<c>SeCreateSymbolicLinkPrivilege</c> on Windows).</p>
+ </item>
</taglist>
</desc>
</func>
@@ -1630,6 +1635,11 @@
<desc>
<p>Sets the current working directory of the file server to
<c><anno>Dir</anno></c>. Returns <c>ok</c> if successful.</p>
+ <p>The functions in the <c>file</c> module usually treat binaries
+ as raw filenames, i.e. they are passed as is even when the encoding
+ of the binary does not agree with <c>file:native_name_encoding()</c>.
+ This function however expects binaries to be encoded according to the
+ value returned by <c>file:native_name_encoding()</c>.</p>
<p>Typical error reasons are:</p>
<taglist>
<tag><c>enoent</c></tag>
@@ -1654,8 +1664,8 @@
<tag><c>no_translation</c></tag>
<item>
<p><c><anno>Dir</anno></c> is a <c>binary()</c> with
- characters coded in ISO-latin-1 and the VM was started
- with the parameter <c>+fnue</c>.</p>
+ characters coded in ISO-latin-1 and the VM is operating
+ with unicode file name encoding.</p>
</item>
</taglist>
<warning>
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index 4a48a5c3d8..50e1cc290c 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -361,7 +361,7 @@ fe80::204:acff:fe17:bf38
</item>
<tag><c>send_dvi</c></tag>
<item>
- <p>Average packet size deviation in bytes received sent from the socket.</p>
+ <p>Average packet size deviation in bytes sent from the socket.</p>
</item>
<tag><c>send_max</c></tag>
<item>
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index 49a93d2c70..00c6bc33d6 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -4,7 +4,7 @@
<appref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -35,7 +35,7 @@
Erlang/OTP consists of Kernel and STDLIB. The Kernel application
contains the following services:</p>
<list type="bulleted">
- <item>application controller, see <c>application(3)</c></item>
+ <item>application controller, see <seealso marker="application">application(3)</seealso></item>
<item><c>code</c></item>
<item><c>disk_log</c></item>
<item><c>dist_ac</c>, distributed application controller</item>
@@ -66,8 +66,8 @@
<section>
<title>Configuration</title>
<p>The following configuration parameters are defined for the Kernel
- application. See <c>app(3)</c> for more information about
- configuration parameters.</p>
+ application. See <seealso marker="app">app(4)</seealso> for more
+ information about configuration parameters.</p>
<taglist>
<tag><c>browser_cmd = string() | {M,F,A}</c></tag>
<item>
@@ -93,7 +93,8 @@
<item><c>Time = integer()>0</c></item>
<item><c>Nodes = [node() | {node(),...,node()}]</c></item>
</list>
- <p>The parameter is described in <c>application(3)</c>, function
+ <p>The parameter is described in
+ <seealso marker="application">application(3)</seealso>, function
<c>load/2</c>.</p>
</item>
<tag><c>dist_auto_connect = Value</c></tag>
@@ -105,11 +106,13 @@
<taglist>
<tag><c>never</c></tag>
<item>Connections are never automatically established, they
- must be explicitly connected. See <c>net_kernel(3)</c>.</item>
+ must be explicitly connected. See
+ <seealso marker="net_kernel">net_kernel(3)</seealso>.</item>
<tag><c>once</c></tag>
<item>Connections will be established automatically, but only
once per node. If a node goes down, it must thereafter be
- explicitly connected. See <c>net_kernel(3)</c>.</item>
+ explicitly connected. See
+ <seealso marker="net_kernel">net_kernel(3)</seealso>.</item>
</taglist>
</item>
<tag><c>permissions = [Perm]</c></tag>
@@ -121,7 +124,8 @@
<item><c>ApplName = atom()</c></item>
<item><c>Bool = boolean()</c></item>
</list>
- <p>Permissions are described in <c>application(3)</c>, function
+ <p>Permissions are described in
+ <seealso marker="application">application(3)</seealso>, function
<c>permit/2</c>.</p>
</item>
<tag><c>error_logger = Value</c></tag>
@@ -149,7 +153,8 @@
</item>
<tag><c>global_groups = [GroupTuple]</c></tag>
<item>
- <p>Defines global groups, see <c>global_group(3)</c>.</p>
+ <p>Defines global groups, see
+ <seealso marker="global_group">global_group(3)</seealso>.</p>
<list type="bulleted">
<item><c>GroupTuple = {GroupName, [Node]} | {GroupName, PublishType, [Node]}</c></item>
<item><c>GroupName = atom()</c></item>
@@ -160,18 +165,19 @@
<tag><c>inet_default_connect_options = [{Opt, Val}]</c></tag>
<item>
<p>Specifies default options for <c>connect</c> sockets,
- see <c>inet(3)</c>.</p>
+ see <seealso marker="inet">inet(3)</seealso>.</p>
</item>
<tag><c>inet_default_listen_options = [{Opt, Val}]</c></tag>
<item>
<p>Specifies default options for <c>listen</c> (and
- <c>accept</c>) sockets, see <c>inet(3)</c>.</p>
+ <c>accept</c>) sockets, see <seealso marker="inet">inet(3)</seealso>.</p>
</item>
<tag><c>{inet_dist_use_interface, ip_address()}</c></tag>
<item>
<p>If the host of an Erlang node has several network interfaces,
this parameter specifies which one to listen on. See
- <c>inet(3)</c> for the type definition of <c>ip_address()</c>.</p>
+ <seealso marker="inet">inet(3)</seealso> for the type definition
+ of <c>ip_address()</c>.</p>
</item>
<tag><c>{inet_dist_listen_min, First}</c></tag>
<item>
@@ -276,7 +282,8 @@ MaxT = TickTime + TickTime / 4</code>
<tag><c>start_boot_server = true | false</c></tag>
<item>
<p>Starts the <c>boot_server</c> if the parameter is <c>true</c>
- (see <c>erl_boot_server(3)</c>). This parameter should be
+ (see <seealso marker="erl_boot_server">erl_boot_server(3)</seealso>).
+ This parameter should be
set to <c>true</c> in an embedded system which uses this
service.</p>
<p>The default value is <c>false</c>.</p>
@@ -296,13 +303,15 @@ MaxT = TickTime + TickTime / 4</code>
<tag><c>start_disk_log = true | false</c></tag>
<item>
<p>Starts the <c>disk_log_server</c> if the parameter is
- <c>true</c> (see <c>disk_log(3)</c>). This parameter should be
+ <c>true</c> (see <seealso marker="disk_log">disk_log(3)</seealso>).
+ This parameter should be
set to true in an embedded system which uses this service.</p>
<p>The default value is <c>false</c>.</p>
</item>
<tag><c>start_pg2 = true | false</c></tag>
<item>
- <p>Starts the <c>pg2</c> server (see <c>pg2(3)</c>) if
+ <p>Starts the <c>pg2</c> server (see
+ <seealso marker="pg2">pg2(3)</seealso>) if
the parameter is <c>true</c>. This parameter should be set to
<c>true</c> in an embedded system which uses this service.</p>
<p>The default value is <c>false</c>.</p>
@@ -310,7 +319,8 @@ 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 <c>timer(3)</c>). This parameter should be
+ <c>true</c> (see <seealso marker="stdlib:timer">timer(3)</seealso>).
+ This parameter should be
set to <c>true</c> in an embedded system which uses this
service.</p>
<p>The default value is <c>false</c>.</p>
@@ -351,6 +361,7 @@ MaxT = TickTime + TickTime / 4</code>
<seealso marker="pg2">pg2(3)</seealso>,
<seealso marker="rpc">rpc(3)</seealso>,
<seealso marker="seq_trace">seq_trace(3)</seealso>,
+ <seealso marker="stdlib:timer">timer(3)</seealso>,
<seealso marker="user">user(3)</seealso></p>
</section>
</appref>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index b2e89ea850..c6538b7d05 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -30,6 +30,203 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 3.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a deadlock possibility in terminate application</p>
+ <p>
+ Own Id: OTP-11171</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug where sendfile would return the wrong error
+ code for a remotely closed socket if the socket was in
+ passive mode. (Thanks to Vincent Siliakus for reporting
+ the bug.)</p>
+ <p>
+ Own Id: OTP-11614</p>
+ </item>
+ <item>
+ <p>
+ The new option <c>persistent</c> is added to
+ <c>application:set_env/4</c> and
+ <c>application:unset_env/3</c>. An environment key set
+ with the <c>persistent</c> option will not be overridden
+ by the ones configured in the application resource file
+ on load. This means persistent values will stick after
+ the application is loaded and also on application reload.
+ (Thanks to José Valim)</p>
+ <p>
+ Own Id: OTP-11708</p>
+ </item>
+ <item>
+ <p>
+ The spec for file:set_cwd/1 is modified to also accept
+ binaries as arguments. This has always been allowed in
+ the code, but it was not reflected in the spec since
+ binaries are mostly used for raw file names. Raw file
+ names are names that are not encoded according to
+ file:native_name_encoding(), and these are not allowed in
+ file:set_cwd/1. The spec is now, however, more allowing
+ in order to avoid unnecessary dialyzer warnings. Raw file
+ names will still fail in runtime with reason
+ 'no_translation'. (Thanks to José Valim)</p>
+ <p>
+ Own Id: OTP-11787</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ heart:set_cmd/1 is updated to allow unicode code points >
+ 255 in the given heart command</p>
+ <p>
+ Own Id: OTP-10843</p>
+ </item>
+ <item>
+ <p> Dialyzer's <c>unmatched_return</c> warnings have been
+ corrected. </p>
+ <p>
+ Own Id: OTP-10908</p>
+ </item>
+ <item>
+ <p>
+ Make erlang:open_port/2 spawn and spawn_executable handle
+ unicode.</p>
+ <p>
+ Own Id: OTP-11105</p>
+ </item>
+ <item>
+ <p>
+ Erlang/OTP has been ported to the realtime operating
+ system OSE. The port supports both smp and non-smp
+ emulator. For details around the port and how to started
+ see the User's Guide in the <seealso
+ marker="ose:ose_intro">ose</seealso> application. </p>
+ <p>
+ Note that not all parts of Erlang/OTP has been ported. </p>
+ <p>
+ Notable things that work are: non-smp and smp emulators,
+ OSE signal interaction, crypto, asn1, run_erl/to_erl,
+ tcp, epmd, distribution and most if not all non-os
+ specific functionality of Erlang.</p>
+ <p>
+ Notable things that does not work are: udp/sctp, os_mon,
+ erl_interface, binding of schedulers.</p>
+ <p>
+ Own Id: OTP-11334</p>
+ </item>
+ <item>
+ <p>
+ Add the {active,N} socket option for TCP, UDP, and SCTP,
+ where N is an integer in the range -32768..32767, to
+ allow a caller to specify the number of data messages to
+ be delivered to the controlling process. Once the
+ socket's delivered message count either reaches 0 or is
+ explicitly set to 0 with inet:setopts/2 or by including
+ {active,0} as an option when the socket is created, the
+ socket transitions to passive ({active, false}) mode and
+ the socket's controlling process receives a message to
+ inform it of the transition. TCP sockets receive
+ {tcp_passive,Socket}, UDP sockets receive
+ {udp_passive,Socket} and SCTP sockets receive
+ {sctp_passive,Socket}. </p>
+ <p>
+ The socket's delivered message counter defaults to 0, but
+ it can be set using {active,N} via any gen_tcp, gen_udp,
+ or gen_sctp function that takes socket options as
+ arguments, or via inet:setopts/2. New N values are added
+ to the socket's current counter value, and negative
+ numbers can be used to reduce the counter value.
+ Specifying a number that would cause the socket's counter
+ value to go above 32767 causes an einval error. If a
+ negative number is specified such that the counter value
+ would become negative, the socket's counter value is set
+ to 0 and the socket transitions to passive mode. If the
+ counter value is already 0 and inet:setopts(Socket,
+ [{active,0}]) is specified, the counter value remains at
+ 0 but the appropriate passive mode transition message is
+ generated for the socket.</p>
+ <p>
+ Thanks to Steve Vinoski</p>
+ <p>
+ Own Id: OTP-11368</p>
+ </item>
+ <item>
+ <p>
+ A call to either the <c>garbage_collect/1</c> BIF or the
+ <c>check_process_code/2</c> BIF may trigger garbage
+ collection of another processes than the process calling
+ the BIF. The previous implementations performed these
+ kinds of garbage collections without considering the
+ internal state of the process being garbage collected. In
+ order to be able to more easily and more efficiently
+ implement yielding native code, these types of garbage
+ collections have been rewritten. A garbage collection
+ like this is now triggered by an asynchronous request
+ signal, the actual garbage collection is performed by the
+ process being garbage collected itself, and finalized by
+ a reply signal to the process issuing the request. Using
+ this approach processes can disable garbage collection
+ and yield without having to set up the heap in a state
+ that can be garbage collected.</p>
+ <p>
+ The <seealso
+ marker="erts:erlang#garbage_collect/2"><c>garbage_collect/2</c></seealso>,
+ and <seealso
+ marker="erts:erlang#check_process_code/3"><c>check_process_code/3</c></seealso>
+ BIFs have been introduced. Both taking an option list as
+ last argument. Using these, one can issue asynchronous
+ requests.</p>
+ <p>
+ <c>code:purge/1</c> and <c>code:soft_purge/1</c> have
+ been rewritten to utilize asynchronous
+ <c>check_process_code</c> requests in order to
+ parallelize work.</p>
+ <p>
+ Characteristics impact: A call to the
+ <c>garbage_collect/1</c> BIF or the
+ <c>check_process_code/2</c> BIF will normally take longer
+ time to complete while the system as a whole wont be as
+ much negatively effected by the operation as before. A
+ call to <c>code:purge/1</c> and <c>code:soft_purge/1</c>
+ may complete faster or slower depending on the state of
+ the system while the system as a whole wont be as much
+ negatively effected by the operation as before.</p>
+ <p>
+ Own Id: OTP-11388 Aux Id: OTP-11535, OTP-11648 </p>
+ </item>
+ <item>
+ <p>
+ Add sync option to file:open/2.</p>
+ <p>
+ The sync option adds the POSIX O_SYNC flag to the open
+ system call on platforms that support the flag or its
+ equivalent, e.g., FILE_FLAG_WRITE_THROUGH on Windows. For
+ platforms that don't support it, file:open/2 returns
+ {error, enotsup} if the sync option is passed in. Thank
+ to Steve Vinoski and Joseph Blomstedt</p>
+ <p>
+ Own Id: OTP-11498</p>
+ </item>
+ <item>
+ <p> The contract of <c>inet:ntoa/1</c> has been
+ corrected. </p> <p> Thanks to Max Treskin. </p>
+ <p>
+ Own Id: OTP-11730</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 2.16.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl
index e32c112e63..77556d1303 100644
--- a/lib/kernel/include/dist.hrl
+++ b/lib/kernel/include/dist.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,3 +37,4 @@
-define(DFLAG_DIST_HDR_ATOM_CACHE,16#2000).
-define(DFLAG_SMALL_ATOM_TAGS, 16#4000).
-define(DFLAG_UTF8_ATOMS, 16#10000).
+-define(DFLAG_MAP_TAG, 16#20000).
diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl
index 76a80553b0..c4bef5188a 100644
--- a/lib/kernel/src/application.erl
+++ b/lib/kernel/src/application.erl
@@ -30,6 +30,8 @@
-export([get_application/0, get_application/1, info/0]).
-export([start_type/0]).
+-export_type([start_type/0]).
+
%%%-----------------------------------------------------------------
-type start_type() :: 'normal'
@@ -58,8 +60,7 @@
%%------------------------------------------------------------------
--callback start(StartType :: normal | {takeover, node()} | {failover, node()},
- StartArgs :: term()) ->
+-callback start(StartType :: start_type(), StartArgs :: term()) ->
{'ok', pid()} | {'ok', pid(), State :: term()} | {'error', Reason :: term()}.
-callback stop(State :: term()) ->
diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl
index ed13035104..daad45b6c2 100644
--- a/lib/kernel/src/application_controller.erl
+++ b/lib/kernel/src/application_controller.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -829,12 +829,12 @@ handle_call({change_application_data, Applications, Config}, _From, S) ->
{reply, Error, S};
{'EXIT', R} ->
{reply, {error, R}, S};
- NewAppls ->
+ {NewAppls, NewConfig} ->
lists:foreach(fun(Appl) ->
ets:insert(ac_tab, {{loaded, Appl#appl.name},
Appl})
end, NewAppls),
- {reply, ok, S#state{conf_data = Config}}
+ {reply, ok, S#state{conf_data = NewConfig}}
end;
handle_call(prep_config_change, _From, S) ->
@@ -1550,18 +1550,19 @@ do_change_apps(Applications, Config, OldAppls) ->
end,
Errors),
- map(fun(Appl) ->
- AppName = Appl#appl.name,
- case is_loaded_app(AppName, Applications) of
- {true, Application} ->
- do_change_appl(make_appl(Application),
- Appl, SysConfig);
-
- %% ignored removed apps - handled elsewhere
- false ->
- Appl
- end
- end, OldAppls).
+ {map(fun(Appl) ->
+ AppName = Appl#appl.name,
+ case is_loaded_app(AppName, Applications) of
+ {true, Application} ->
+ do_change_appl(make_appl(Application),
+ Appl, SysConfig);
+
+ %% ignored removed apps - handled elsewhere
+ false ->
+ Appl
+ end
+ end, OldAppls),
+ SysConfig}.
is_loaded_app(AppName, [{application, AppName, App} | _]) ->
{true, {application, AppName, App}};
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index fc7ac08699..dd06affd70 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -63,7 +63,10 @@ init(Ref, Parent, [Root,Mode0]) ->
process_flag(trap_exit, true),
Db = ets:new(code, [private]),
- foreach(fun (M) -> ets:insert(Db, {M,preloaded}) end, erlang:pre_loaded()),
+ foreach(fun (M) ->
+ %% Pre-loaded modules are always sticky.
+ ets:insert(Db, [{M,preloaded},{{sticky,M},true}])
+ end, erlang:pre_loaded()),
ets:insert(Db, init:fetch_loaded()),
Mode =
@@ -988,7 +991,7 @@ try_archive_subdirs(_Archive, Base, []) ->
%% the complete directory name.
%%
del_path(Name0,Path,NameDb) ->
- case catch to_list(Name0)of
+ case catch filename:join([to_list(Name0)]) of
{'EXIT',_} ->
{{error,bad_name},Path};
Name ->
@@ -1165,7 +1168,7 @@ stick_dir(Dir, Stick, St) ->
true ->
foreach(fun (M) -> ets:insert(Db, {{sticky,M},true}) end, Mods);
false ->
- foreach(fun (M) -> ets:delete(Db, {sticky,M}) end, Mods)
+ foreach(fun (M) -> do_unstick_mod(Db, M) end, Mods)
end;
Error ->
Error
@@ -1177,6 +1180,15 @@ stick_mod(M, Stick, St) ->
true ->
ets:insert(Db, {{sticky,M},true});
false ->
+ do_unstick_mod(Db, M)
+ end.
+
+do_unstick_mod(Db, M) ->
+ case ets:lookup(Db, M) of
+ [{M,preloaded}] ->
+ %% Never unstick pre-loaded modules.
+ true;
+ _ ->
ets:delete(Db, {sticky,M})
end.
diff --git a/lib/kernel/src/disk_log_server.erl b/lib/kernel/src/disk_log_server.erl
index 684ea5b5db..45334912eb 100644
--- a/lib/kernel/src/disk_log_server.erl
+++ b/lib/kernel/src/disk_log_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -199,7 +199,7 @@ do_open({open, W, #arg{name = Name}=A}=Req, From, State) ->
false when W =:= local ->
case A#arg.distributed of
{true, Nodes} ->
- Fun = fun() -> open_distr_rpc(Nodes, A, From) end,
+ Fun = open_distr_rpc_fun(Nodes, A, From),
_Pid = spawn(Fun),
%% No pending reply is expected, but don't reply yet.
{pending, State};
@@ -225,11 +225,15 @@ do_open({open, W, #arg{name = Name}=A}=Req, From, State) ->
end
end.
+-spec open_distr_rpc_fun([node()], _, _) -> % XXX: underspecified
+ fun(() -> no_return()).
+
+open_distr_rpc_fun(Nodes, A, From) ->
+ fun() -> open_distr_rpc(Nodes, A, From) end.
+
%% Spawning a process is a means to avoid deadlock when
%% disk_log_servers mutually open disk_logs.
--spec open_distr_rpc([node()], _, _) -> no_return(). % XXX: underspecified
-
open_distr_rpc(Nodes, A, From) ->
{AllReplies, BadNodes} = rpc:multicall(Nodes, ?MODULE, dist_open, [A]),
{Ok, Bad} = cr(AllReplies, [], []),
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index fc50ec6717..b127fe2e33 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -116,7 +116,8 @@ make_this_flags(RequestType, OtherNode) ->
?DFLAG_UNICODE_IO bor
?DFLAG_DIST_HDR_ATOM_CACHE bor
?DFLAG_SMALL_ATOM_TAGS bor
- ?DFLAG_UTF8_ATOMS).
+ ?DFLAG_UTF8_ATOMS bor
+ ?DFLAG_MAP_TAG).
handshake_other_started(#hs_data{request_type=ReqType}=HSData0) ->
{PreOtherFlags,Node,Version} = recv_name(HSData0),
diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl
index f7a815882b..ef605d0bfe 100644
--- a/lib/kernel/src/erts_debug.erl
+++ b/lib/kernel/src/erts_debug.erl
@@ -182,6 +182,11 @@ size(Tuple, Seen0, Sum0) when is_tuple(Tuple) ->
Sum = Sum0 + 1 + tuple_size(Tuple),
tuple_size(1, tuple_size(Tuple), Tuple, Seen, Sum)
end;
+size(Map, Seen0, Sum) when is_map(Map) ->
+ case remember_term(Map, Seen0) of
+ seen -> {Sum,Seen0};
+ Seen -> map_size(Map, Seen, Sum)
+ end;
size(Fun, Seen0, Sum) when is_function(Fun) ->
case remember_term(Fun, Seen0) of
seen -> {Sum,Seen0};
@@ -203,6 +208,12 @@ tuple_size(I, Sz, Tuple, Seen0, Sum0) ->
{Sum,Seen} = size(element(I, Tuple), Seen0, Sum0),
tuple_size(I+1, Sz, Tuple, Seen, Sum).
+map_size(Map,Seen0,Sum0) ->
+ Kt = erts_internal:map_to_tuple_keys(Map),
+ Vs = maps:values(Map),
+ {Sum1,Seen1} = size(Kt,Seen0,Sum0),
+ fold_size(Vs,Seen1,Sum1+length(Vs)+3).
+
fun_size(Fun, Seen, Sum) ->
case erlang:fun_info(Fun, type) of
{type,external} ->
@@ -210,14 +221,14 @@ fun_size(Fun, Seen, Sum) ->
{type,local} ->
Sz = erts_debug:flat_size(fun() -> ok end),
{env,Env} = erlang:fun_info(Fun, env),
- fun_size_1(Env, Seen, Sum+Sz+length(Env))
+ fold_size(Env, Seen, Sum+Sz+length(Env))
end.
-fun_size_1([H|T], Seen0, Sum0) ->
+fold_size([H|T], Seen0, Sum0) ->
{Sum,Seen} = size(H, Seen0, Sum0),
- fun_size_1(T, Seen, Sum);
-fun_size_1([], Seen, Sum) -> {Sum,Seen}.
-
+ fold_size(T, Seen, Sum);
+fold_size([], Seen, Sum) -> {Sum,Seen}.
+
remember_term(Term, Seen) ->
case gb_trees:lookup(Term, Seen) of
none -> gb_trees:insert(Term, [Term], Seen);
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index 23cf74f80f..20b703e084 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -199,7 +199,8 @@ get_cwd(Drive) ->
check_and_call(get_cwd, [file_name(Drive)]).
-spec set_cwd(Dir) -> ok | {error, Reason} when
- Dir :: name(),
+ Dir :: name() | EncodedBinary,
+ EncodedBinary :: binary(),
Reason :: posix() | badarg | no_translation.
set_cwd(Dirname) ->
diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl
index ef878b8d0c..0a4edea452 100644
--- a/lib/kernel/src/global.erl
+++ b/lib/kernel/src/global.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1513,14 +1513,18 @@ delete_global_name(_Name, _Pid) ->
-record(him, {node, locker, vsn, my_tag}).
start_the_locker(DoTrace) ->
- spawn_link(fun() -> init_the_locker(DoTrace) end).
-
-init_the_locker(DoTrace) ->
- process_flag(trap_exit, true), % needed?
- S0 = #multi{do_trace = DoTrace},
- S1 = update_locker_known({add, get_known()}, S0),
- loop_the_locker(S1),
- erlang:error(locker_exited).
+ spawn_link(init_the_locker_fun(DoTrace)).
+
+-spec init_the_locker_fun(boolean()) -> fun(() -> no_return()).
+
+init_the_locker_fun(DoTrace) ->
+ fun() ->
+ process_flag(trap_exit, true), % needed?
+ S0 = #multi{do_trace = DoTrace},
+ S1 = update_locker_known({add, get_known()}, S0),
+ loop_the_locker(S1),
+ erlang:error(locker_exited)
+ end.
loop_the_locker(S) ->
?trace({loop_the_locker,S}),
diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl
index e111cb800e..e5928c7b63 100644
--- a/lib/kernel/src/hipe_unified_loader.erl
+++ b/lib/kernel/src/hipe_unified_loader.erl
@@ -194,6 +194,13 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
CodeSize, CodeBinary, Refs,
0,[] % ColdSize, CRrefs
] = binary_to_term(Bin),
+ ?debug_msg("***** ErLLVM *****~nVersion: ~s~nCheckSum: ~w~nConstAlign: ~w~n" ++
+ "ConstSize: ~w~nConstMap: ~w~nLabelMap: ~w~nExportMap ~w~nRefs ~w~n",
+ [Version, CheckSum, ConstAlign, ConstSize, ConstMap, LabelMap, ExportMap,
+ Refs]),
+ %% Write HiPE binary code to a file in the current directory in order to
+ %% debug by disassembling.
+ %% file:write_file("erl.o", CodeBinary, [binary]),
%% Check that we are loading up-to-date code.
version_check(Version, Mod),
case hipe_bifs:check_crc(CheckSum) of
@@ -203,6 +210,7 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
"please regenerate native code for this runtime system\n", [Mod]),
bad_crc;
true ->
+ put(closures_to_patch, []),
%% Create data segment
{ConstAddr,ConstMap2} =
create_data_segment(ConstAlign, ConstSize, ConstMap),
@@ -220,24 +228,33 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
{MFAs,Addresses} = exports(ExportMap, CodeAddress),
%% Remove references to old versions of the module.
ReferencesToPatch = get_refs_from(MFAs, []),
+ %% io:format("References to patch: ~w~n", [ReferencesToPatch]),
ok = remove_refs_from(MFAs),
%% Patch all dynamic references in the code.
%% Function calls, Atoms, Constants, System calls
ok = patch(Refs, CodeAddress, ConstMap2, Addresses, TrampolineMap),
+
%% Tell the system where the loaded funs are.
%% (patches the BEAM code to redirect to native.)
case Beam of
[] ->
+ %% This module was previously loaded as BEAM code during system
+ %% start-up before the code server has started (-enable-native-libs
+ %% is active), so we must now patch the pre-existing entries in the
+ %% fun table with the native code addresses for all closures.
+ lists:foreach(fun({FE, DestAddress}) ->
+ hipe_bifs:set_native_address_in_fe(FE, DestAddress)
+ end, erase(closures_to_patch)),
export_funs(Addresses),
ok;
BeamBinary when is_binary(BeamBinary) ->
%% Find all closures in the code.
+ [] = erase(closures_to_patch), %Clean up, assertion.
ClosurePatches = find_closure_patches(Refs),
AddressesOfClosuresToPatch =
calculate_addresses(ClosurePatches, CodeAddress, Addresses),
export_funs(Addresses),
- export_funs(Mod, BeamBinary, Addresses, AddressesOfClosuresToPatch),
- ok
+ export_funs(Mod, BeamBinary, Addresses, AddressesOfClosuresToPatch)
end,
%% Redirect references to the old module to the new module's BEAM stub.
patch_to_emu_step2(OldReferencesToPatch),
@@ -245,6 +262,9 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
%% The call to export_funs/1 above updated the native addresses
%% for the targets, so passing 'Addresses' is not needed.
redirect(ReferencesToPatch),
+ %% Final clean up.
+ _ = erase(hipe_patch_closures),
+ _ = erase(hipe_assert_code_area),
?debug_msg("****************Loader Finished****************\n", []),
{module,Mod} % for compatibility with code:load_file/1
end.
@@ -562,12 +582,17 @@ patch_closure(DestMFA, Uniq, Index, Address, Addresses) ->
case get(hipe_patch_closures) of
false ->
[]; % This is taken care of when registering the module.
- true -> % We are not loading a module patch these closures
+ true ->
+ %% We are replacing a previosly loaded BEAM module with native code,
+ %% so we must reference the pre-existing entries in the fun table
+ %% from the native code. We must delay actually patching the native
+ %% address into the fun entry to ensure that the native code cannot
+ %% be called until it has been completely fixed up.
RemoteOrLocal = local, % closure code refs are local
DestAddress = get_native_address(DestMFA, Addresses, RemoteOrLocal),
BEAMAddress = hipe_bifs:fun_to_address(DestMFA),
- FE = hipe_bifs:make_fe(DestAddress, mod(DestMFA),
- {Uniq, Index, BEAMAddress}),
+ FE = hipe_bifs:get_fe(mod(DestMFA), {Uniq, Index, BEAMAddress}),
+ put(closures_to_patch, [{FE,DestAddress}|get(closures_to_patch)]),
?debug_msg("Patch FE(~w) to 0x~.16b->0x~.16b (emu:0x~.16b)\n",
[DestMFA, FE, DestAddress, BEAMAddress]),
?ASSERT(assert_local_patch(Address)),
diff --git a/lib/kernel/src/inet_config.erl b/lib/kernel/src/inet_config.erl
index 2461f3ff25..fdc244f959 100644
--- a/lib/kernel/src/inet_config.erl
+++ b/lib/kernel/src/inet_config.erl
@@ -197,6 +197,9 @@ do_load_resolv({win32,Type}, longnames) ->
win32_load_from_registry(Type),
inet_db:set_lookup([native]);
+do_load_resolv({ose,_}, _) ->
+ inet_db:set_lookup([file]);
+
do_load_resolv(_, _) ->
inet_db:set_lookup([native]).
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index cb8c98ab06..5658c6b6cf 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -114,6 +114,7 @@
pg2]},
{applications, []},
{env, [{error_logger, tty}]},
- {mod, {kernel, []}}
+ {mod, {kernel, []}},
+ {runtime_dependencies, ["erts-6.0", "stdlib-2.0", "sasl-2.4"]}
]
}.
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index 9ffa9adeab..187fd0001b 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -67,7 +67,7 @@ unsetenv(_) ->
%%% End of BIFs
-spec type() -> {Osfamily, Osname} when
- Osfamily :: unix | win32,
+ Osfamily :: unix | win32 | ose,
Osname :: atom().
type() ->
@@ -230,7 +230,9 @@ unix_cmd(Cmd) ->
%% and the commands are read from standard input. We set the
%% $1 parameter for easy identification of the resident shell.
%%
--define(SHELL, "/bin/sh -s unix:cmd 2>&1").
+-define(ROOT, "/").
+-define(ROOT_ANDROID, "/system").
+-define(SHELL, "bin/sh -s unix:cmd 2>&1").
-define(PORT_CREATOR_NAME, os_cmd_port_creator).
%%
@@ -280,7 +282,12 @@ start_port_srv(Request) ->
end.
start_port_srv_handle({Ref,Client}) ->
- Reply = try open_port({spawn, ?SHELL},[stream]) of
+ Path = case lists:reverse(erlang:system_info(system_architecture)) of
+ % androideabi
+ "ibaediordna" ++ _ -> filename:join([?ROOT_ANDROID, ?SHELL]);
+ _ -> filename:join([?ROOT, ?SHELL])
+ end,
+ Reply = try open_port({spawn, Path},[stream]) of
Port when is_port(Port) ->
(catch port_connect(Port, Client)),
unlink(Port),
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index c6cbd1a0ef..4901206c8e 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -1076,10 +1076,13 @@ otp_1586(Conf) when is_list(Conf) ->
{ok, Fd} = file:open(filename:join(Dir, "app5.app"), [write]),
w_app5(Fd),
file:close(Fd),
- code:add_patha(Dir),
- ok = application:load(app4()),
- ok = application:unload(app4),
- ok.
+ try
+ true = code:add_patha(Dir),
+ ok = application:load(app4()),
+ ok = application:unload(app4)
+ after
+ _ = code:del_path(Dir)
+ end.
%%-----------------------------------------------------------------
%% Ticket: OTP-2078
@@ -1949,14 +1952,22 @@ config_change(Conf) when is_list(Conf) ->
%% Find out application data from boot script
Boot = filename:join([code:root_dir(), "bin", "start.boot"]),
{ok, Bin} = file:read_file(Boot),
- Appls = get_appls(binary_to_term(Bin)),
+ Appls0 = get_appls(binary_to_term(Bin)),
+
+ %% And add app1 in order to test OTP-11864 - included config files
+ %% not read for new (not already loaded) applications
+ Appls = [app1() | Appls0],
%% Simulate contents of "sys.config"
Config = [{stdlib, [{par1,sys},{par2,sys}]},
"t1",
"t2.config",
filename:join([DataDir, "subdir", "t3"]),
- {stdlib, [{par6,sys}]}],
+ {stdlib, [{par6,sys}]},
+ "t4.config"],
+
+ %% Check that app1 is not loaded
+ false = lists:keymember(app1,1,application:loaded_applications()),
%% Order application_controller to update configuration
ok = application_controller:change_application_data(Appls,
@@ -1971,6 +1982,13 @@ config_change(Conf) when is_list(Conf) ->
{value, {par5,t3}} = lists:keysearch(par5, 1, Env),
{value, {par6,sys}} = lists:keysearch(par6, 1, Env),
+ %% Check that app1 parameters are correctly set after loading
+ [] = application:get_all_env(app1),
+ application:load(app1()),
+ App1Env = application:get_all_env(app1),
+ {value, {par1,t4}} = lists:keysearch(par1, 1, App1Env),
+ application:unload(app1),
+
ok = file:set_cwd(CWD).
%% This function is stolen from SASL module release_handler, OTP R10B
@@ -1989,6 +2007,7 @@ get_appls([_ | T], Res) ->
get_appls([], Res) ->
Res.
+
persistent_env(suite) ->
[];
persistent_env(doc) ->
diff --git a/lib/kernel/test/application_SUITE_data/t4.config b/lib/kernel/test/application_SUITE_data/t4.config
new file mode 100644
index 0000000000..8b2bc52c01
--- /dev/null
+++ b/lib/kernel/test/application_SUITE_data/t4.config
@@ -0,0 +1 @@
+[{app1, [{par1,t4}]}]. \ No newline at end of file
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 42b81d16b3..afedc17e57 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -37,8 +37,7 @@
native_early_modules/1, get_mode/1]).
-export([init_per_testcase/2, end_per_testcase/2,
- init_per_suite/1, end_per_suite/1,
- sticky_compiler/1]).
+ init_per_suite/1, end_per_suite/1]).
%% error_logger
-export([init/1,
@@ -55,7 +54,7 @@ all() ->
delete, purge, purge_many_exits, soft_purge, is_loaded, all_loaded,
load_binary, dir_req, object_code, set_path_file,
upgrade,
- pa_pz_option, add_del_path, dir_disappeared,
+ sticky_dir, pa_pz_option, add_del_path, dir_disappeared,
ext_mod_dep, clash, load_cached, start_node_with_cache,
add_and_rehash, where_is_file_no_cache,
where_is_file_cached, purge_stacktrace, mult_lib_roots,
@@ -219,6 +218,13 @@ del_path(suite) -> [];
del_path(doc) -> [];
del_path(Config) when is_list(Config) ->
P = code:get_path(),
+ try
+ del_path_1(P)
+ after
+ code:set_path(P)
+ end.
+
+del_path_1(P) ->
test_server:format("Initial code:get_path()=~p~n",[P]),
{'EXIT',_} = (catch code:del_path(3)),
false = code:del_path(my_dummy_name),
@@ -226,19 +232,22 @@ del_path(Config) when is_list(Config) ->
Dir = filename:join([code:lib_dir(kernel),"ebin"]),
test_server:format("kernel dir: ~p~n",[Dir]),
-
true = code:del_path(kernel),
NewP = code:get_path(),
test_server:format("Path after removing 'kernel':~p~n",[NewP]),
ReferenceP = lists:delete(Dir,P),
test_server:format("Reference path:~p~n",[ReferenceP]),
NewP = ReferenceP, % check that dir is deleted
+ code:set_path(P),
+ %% An superfluous "/" should also work.
+ true = code:del_path("kernel/"),
+ NewP = ReferenceP, % check that dir is deleted
code:set_path(P),
+
true = code:del_path(Dir),
NewP1 = code:get_path(),
NewP1 = lists:delete(Dir,P), % check that dir is deleted
- code:set_path(P),
ok.
replace_path(suite) -> [];
@@ -577,35 +586,42 @@ sticky_dir(suite) -> [];
sticky_dir(doc) -> ["Test that a module with the same name as a module in ",
"a sticky directory cannot be loaded."];
sticky_dir(Config) when is_list(Config) ->
- MyDir=filename:dirname(code:which(?MODULE)),
- {ok, Node}=?t:start_node(sticky_dir, slave,[{args, "-pa \""++MyDir++"\""}]),
- File=filename:join([?config(data_dir, Config), "calendar"]),
- Ret=rpc:call(Node, ?MODULE, sticky_compiler, [File]),
+ Pa = filename:dirname(code:which(?MODULE)),
+ {ok,Node} = ?t:start_node(sticky_dir, slave, [{args,"-pa "++Pa}]),
+ Mods = [code,lists,erlang,init],
+ OutDir = filename:join(?config(priv_dir, Config), sticky_dir),
+ _ = file:make_dir(OutDir),
+ Ret = rpc:call(Node, erlang, apply,
+ [fun sticky_compiler/2,[Mods,OutDir]]),
case Ret of
- fail ->
- ?t:fail("c:c allowed a sticky module to be compiled and loaded.");
- ok ->
+ [] ->
ok;
Other ->
- test_server:format("Other: ~p",[Other])
+ io:format("~p\n", [Other]),
+ ?t:fail()
end,
- ?t:stop_node(Node).
+ ?t:stop_node(Node),
+ ok.
-sticky_compiler(File) ->
- Compiled=File++code:objfile_extension(),
- Dir=filename:dirname(File),
- code:add_patha(Dir),
- file:delete(Compiled),
- case c:c(File, [{outdir, Dir}]) of
- {ok, Module} ->
- case catch Module:test(apa) of
- {error, _} ->
- fail;
- {'EXIT', _} ->
- ok
- end;
- Other ->
- test_server:format("c:c(~p) returned: ~p",[File, Other]),
+sticky_compiler(Files, PrivDir) ->
+ code:add_patha(PrivDir),
+ Rets = [do_sticky_compile(F, PrivDir) || F <- Files],
+ [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
end.
diff --git a/lib/kernel/test/code_SUITE_data/calendar.erl b/lib/kernel/test/code_SUITE_data/calendar.erl
deleted file mode 100644
index c1a4a1c12a..0000000000
--- a/lib/kernel/test/code_SUITE_data/calendar.erl
+++ /dev/null
@@ -1,23 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(calendar).
--export([test/1]).
-
-test(apa) ->
- {error, this_function_should_not_be_called}.
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 6b52493f46..f6d6cd94ab 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -428,7 +428,13 @@ make_del_dir(Config) when is_list(Config) ->
% because there are processes having that directory as current.
?line ok = ?FILE_MODULE:make_dir(NewDir),
?line {ok,CurrentDir} = file:get_cwd(),
- ?line ok = ?FILE_MODULE:set_cwd(NewDir),
+ case {os:type(), length(NewDir) >= 260 } of
+ {{win32,_}, true} ->
+ io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH)\n", []),
+ io:format("\nNewDir = ~p\n", [NewDir]);
+ _ ->
+ ?line ok = ?FILE_MODULE:set_cwd(NewDir)
+ end,
try
%% Check that we get an error when trying to create...
%% a deep directory
@@ -485,32 +491,39 @@ cur_dir_0(Config) when is_list(Config) ->
atom_to_list(?MODULE)
++"_curdir"),
?line ok = ?FILE_MODULE:make_dir(NewDir),
- ?line io:format("cd to ~s",[NewDir]),
- ?line ok = ?FILE_MODULE:set_cwd(NewDir),
-
- %% Create a file in the new current directory, and check that it
- %% really is created there
- ?line UncommonName = "uncommon.fil",
- ?line {ok,Fd} = ?FILE_MODULE:open(UncommonName,read_write),
- ?line ok = ?FILE_MODULE:close(Fd),
- ?line {ok,NewDirFiles} = ?FILE_MODULE:list_dir("."),
- ?line true = lists:member(UncommonName,NewDirFiles),
-
- %% Delete the directory and return to the old current directory
- %% and check that the created file isn't there (too!)
- ?line expect({error, einval}, {error, eacces},
- ?FILE_MODULE:del_dir(NewDir)),
- ?line ?FILE_MODULE:delete(UncommonName),
- ?line {ok,[]} = ?FILE_MODULE:list_dir("."),
- ?line ok = ?FILE_MODULE:set_cwd(Dir1),
- ?line io:format("cd back to ~s",[Dir1]),
- ?line ok = ?FILE_MODULE:del_dir(NewDir),
- ?line {error, enoent} = ?FILE_MODULE:set_cwd(NewDir),
- ?line ok = ?FILE_MODULE:set_cwd(Dir1),
- ?line io:format("cd back to ~s",[Dir1]),
- ?line {ok,OldDirFiles} = ?FILE_MODULE:list_dir("."),
- ?line false = lists:member(UncommonName,OldDirFiles),
-
+ case {os:type(), length(NewDir) >= 260} of
+ {{win32,_}, true} ->
+ io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH):\n"),
+ io:format("\nNewDir = ~p\n", [NewDir]);
+ _ ->
+ io:format("cd to ~s",[NewDir]),
+ ok = ?FILE_MODULE:set_cwd(NewDir),
+
+ %% Create a file in the new current directory, and check that it
+ %% really is created there
+ UncommonName = "uncommon.fil",
+ {ok,Fd} = ?FILE_MODULE:open(UncommonName,read_write),
+ ok = ?FILE_MODULE:close(Fd),
+ {ok,NewDirFiles} = ?FILE_MODULE:list_dir("."),
+ true = lists:member(UncommonName,NewDirFiles),
+
+ %% Delete the directory and return to the old current directory
+ %% and check that the created file isn't there (too!)
+ expect({error, einval}, {error, eacces},
+ ?FILE_MODULE:del_dir(NewDir)),
+ ?FILE_MODULE:delete(UncommonName),
+ {ok,[]} = ?FILE_MODULE:list_dir("."),
+ ok = ?FILE_MODULE:set_cwd(Dir1),
+ io:format("cd back to ~s",[Dir1]),
+
+ ok = ?FILE_MODULE:del_dir(NewDir),
+ {error, enoent} = ?FILE_MODULE:set_cwd(NewDir),
+ ok = ?FILE_MODULE:set_cwd(Dir1),
+ io:format("cd back to ~s",[Dir1]),
+ {ok,OldDirFiles} = ?FILE_MODULE:list_dir("."),
+ false = lists:member(UncommonName,OldDirFiles)
+ end,
+
%% Try doing some bad things
?line {error, badarg} = ?FILE_MODULE:set_cwd({foo,bar}),
?line {error, enoent} = ?FILE_MODULE:set_cwd(""),
@@ -1982,7 +1995,6 @@ names(Config) when is_list(Config) ->
?line Name1 = filename:join(RootDir, FileName),
?line Name2 = [RootDir,"/","foo1",".","fil"],
?line Name3 = [RootDir,"/",foo,$1,[[[],[],'.']],"f",il],
- ?line Name4 = list_to_atom(Name1),
?line {ok,Fd0} = ?FILE_MODULE:open(Name1,write),
?line ok = ?FILE_MODULE:close(Fd0),
@@ -1995,23 +2007,33 @@ names(Config) when is_list(Config) ->
?line ok = ?FILE_MODULE:close(Fd2),
?line {ok,Fd3} = ?FILE_MODULE:open(Name3,read),
?line ok = ?FILE_MODULE:close(Fd3),
- ?line {ok,Fd4} = ?FILE_MODULE:open(Name4,read),
- ?line ok = ?FILE_MODULE:close(Fd4),
+ case length(Name1) > 255 of
+ true ->
+ io:format("Path too long for an atom:\n\n~p\n", [Name1]);
+ false ->
+ Name4 = list_to_atom(Name1),
+ {ok,Fd4} = ?FILE_MODULE:open(Name4,read),
+ ok = ?FILE_MODULE:close(Fd4)
+ end,
%% Try some path names
?line Path1 = RootDir,
?line Path2 = [RootDir],
?line Path3 = ['',[],[RootDir,[[]]]],
- ?line Path4 = list_to_atom(Path1),
?line {ok,Fd11,_} = ?FILE_MODULE:path_open([Path1],FileName,read),
?line ok = ?FILE_MODULE:close(Fd11),
?line {ok,Fd12,_} = ?FILE_MODULE:path_open([Path2],FileName,read),
?line ok = ?FILE_MODULE:close(Fd12),
?line {ok,Fd13,_} = ?FILE_MODULE:path_open([Path3],FileName,read),
?line ok = ?FILE_MODULE:close(Fd13),
- ?line {ok,Fd14,_} = ?FILE_MODULE:path_open([Path4],FileName,read),
- ?line ok = ?FILE_MODULE:close(Fd14),
-
+ case length(Path1) > 255 of
+ true->
+ io:format("Path too long for an atom:\n\n~p\n", [Path1]);
+ false ->
+ Path4 = list_to_atom(Path1),
+ {ok,Fd14,_} = ?FILE_MODULE:path_open([Path4],FileName,read),
+ ok = ?FILE_MODULE:close(Fd14)
+ end,
?line [] = flush(),
?line test_server:timetrap_cancel(Dog),
ok.
@@ -2673,6 +2695,9 @@ symlinks(Config) when is_list(Config) ->
case ?FILE_MODULE:make_symlink(Name, Alias) of
{error, enotsup} ->
{skipped, "Links not supported on this platform"};
+ {error, eperm} ->
+ {win32,_} = os:type(),
+ {skipped, "Windows user not privileged to create symlinks"};
ok ->
?line {ok, Info1} = ?FILE_MODULE:read_file_info(Name),
?line {ok, Info1} = ?FILE_MODULE:read_file_info(Alias),
@@ -3599,7 +3624,11 @@ otp_10852(Config) when is_list(Config) ->
ok = rpc_call(Node, list_dir_all, [B]),
ok = rpc_call(Node, read_file, [B]),
ok = rpc_call(Node, make_link, [B,B]),
- ok = rpc_call(Node, make_symlink, [B,B]),
+ case rpc_call(Node, make_symlink, [B,B]) of
+ ok -> ok;
+ {error, E} when (E =:= enotsup) or (E =:= eperm) ->
+ {win32,_} = os:type()
+ end,
ok = rpc_call(Node, delete, [B]),
ok = rpc_call(Node, make_dir, [B]),
ok = rpc_call(Node, del_dir, [B]),
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index ee8bfcceb1..2df4bf7c95 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -50,6 +50,14 @@
oct_acceptor/1,
otp_7731_server/1, zombie_server/2, do_iter_max_socks/2]).
+init_per_testcase(wrapping_oct, Config) when is_list(Config) ->
+ Dog = case os:type() of
+ {ose,_} ->
+ test_server:timetrap(test_server:minutes(20));
+ _Else ->
+ test_server:timetrap(test_server:seconds(600))
+ end,
+ [{watchdog, Dog}|Config];
init_per_testcase(iter_max_socks, Config) when is_list(Config) ->
Dog = case os:type() of
{win32,_} ->
@@ -58,6 +66,17 @@ init_per_testcase(iter_max_socks, Config) when is_list(Config) ->
test_server:timetrap(test_server:seconds(240))
end,
[{watchdog, Dog}|Config];
+init_per_testcase(accept_system_limit, Config) when is_list(Config) ->
+ case os:type() of
+ {ose,_} ->
+ {skip,"Skip in OSE"};
+ _ ->
+ Dog = test_server:timetrap(test_server:seconds(240)),
+ [{watchdog,Dog}|Config]
+ end;
+init_per_testcase(wrapping_oct, Config) when is_list(Config) ->
+ Dog = test_server:timetrap(test_server:seconds(600)),
+ [{watchdog, Dog}|Config];
init_per_testcase(_Func, Config) when is_list(Config) ->
Dog = test_server:timetrap(test_server:seconds(240)),
[{watchdog, Dog}|Config].
@@ -537,7 +556,6 @@ otp_3924(Config) when is_list(Config) ->
otp_3924_1(MaxDelay).
otp_3924_1(MaxDelay) ->
- Dog = test_server:timetrap(test_server:seconds(240)),
?line {ok, Node} = start_node(otp_3924),
?line DataLen = 100*1024,
?line Data = otp_3924_data(DataLen),
@@ -548,7 +566,6 @@ otp_3924_1(MaxDelay) ->
?line ok = otp_3924(MaxDelay, Node, Data, DataLen, N)
end),
?line test_server:stop_node(Node),
- test_server:timetrap_cancel(Dog),
ok.
otp_3924(MaxDelay, Node, Data, DataLen, N) ->
@@ -1953,7 +1970,9 @@ accept_system_limit(doc) ->
accept_system_limit(Config) when is_list(Config) ->
?line {ok, LS} = gen_tcp:listen(0, []),
?line {ok, TcpPort} = inet:port(LS),
- ?line Connector = spawn_link(fun () -> connector(TcpPort) end),
+ Me = self(),
+ ?line Connector = spawn_link(fun () -> connector(TcpPort, Me) end),
+ receive {Connector, sync} -> Connector ! {self(), continue} end,
?line ok = acceptor(LS, false, []),
?line Connector ! stop,
ok.
@@ -1970,8 +1989,10 @@ acceptor(LS, GotSL, A) ->
error
end.
-connector(TcpPort) ->
+connector(TcpPort, Tester) ->
ManyPorts = open_ports([]),
+ Tester ! {self(), sync},
+ receive {Tester, continue} -> timer:sleep(100) end,
ConnF = fun (Port) ->
case catch gen_tcp:connect({127,0,0,1}, TcpPort, []) of
{ok, Sock} ->
@@ -2705,13 +2726,11 @@ wrapping_oct(doc) ->
wrapping_oct(suite) ->
[];
wrapping_oct(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(600)),
{ok,Sock} = gen_tcp:listen(0,[{active,false},{mode,binary}]),
{ok,Port} = inet:port(Sock),
spawn_link(?MODULE,oct_acceptor,[Sock]),
Res = oct_datapump(Port,16#1FFFFFFFF),
gen_tcp:close(Sock),
- test_server:timetrap_cancel(Dog),
ok = Res,
ok.
diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl
index 320b23bea1..35d3b75b34 100644
--- a/lib/kernel/test/heart_SUITE.erl
+++ b/lib/kernel/test/heart_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -93,10 +93,10 @@ start_check(Type, Name) ->
start_check(Type, Name, []).
start_check(Type, Name, Envs) ->
Args = case ?t:os_type() of
- {win32,_} ->
- "-heart " ++ env_encode([{"HEART_COMMAND", no_reboot}|Envs]);
+ {win32,_} ->
+ "+t50000 -heart " ++ env_encode([{"HEART_COMMAND", no_reboot}|Envs]);
_ ->
- "-heart " ++ env_encode(Envs)
+ "+t50000 -heart " ++ env_encode(Envs)
end,
{ok, Node} = case Type of
loose ->
diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl
index 78f5e93fc3..1884e8cf58 100644
--- a/lib/kernel/test/kernel_SUITE.erl
+++ b/lib/kernel/test/kernel_SUITE.erl
@@ -95,10 +95,10 @@ appup_tests(App,{OkVsns,NokVsns}) ->
ok.
create_test_vsns(App) ->
- This = erlang:system_info(otp_release),
- FirstMajor = previous_major(This),
+ ThisMajor = erlang:system_info(otp_release),
+ FirstMajor = previous_major(ThisMajor),
SecondMajor = previous_major(FirstMajor),
- Ok = app_vsn(App,[FirstMajor]),
+ Ok = app_vsn(App,[ThisMajor,FirstMajor]),
Nok0 = app_vsn(App,[SecondMajor]),
Nok = case Ok of
[Ok1|_] ->
@@ -109,9 +109,9 @@ create_test_vsns(App) ->
{Ok,Nok}.
previous_major("17") ->
- "r16";
-previous_major("r"++Rel) ->
- "r"++previous_major(Rel);
+ "r16b";
+previous_major("r16b") ->
+ "r15b";
previous_major(Rel) ->
integer_to_list(list_to_integer(Rel)-1).
diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl
index 3e6a85eadd..05bd5b3a3d 100644
--- a/lib/kernel/test/prim_file_SUITE.erl
+++ b/lib/kernel/test/prim_file_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -183,7 +183,6 @@ time_dist({_D1, _T1} = DT1, {_D2, _T2} = DT2) ->
read_write_file(suite) -> [];
read_write_file(doc) -> [];
read_write_file(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -232,7 +231,6 @@ read_write_file(Config) when is_list(Config) ->
?line {ok,Bin5} = ?PRIM_FILE:read_file(Name),
?line {Bin1,Bin2} = split_binary(Bin5,byte_size(Bin1)),
- ?line test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -254,7 +252,6 @@ make_del_dir_b(Config) when is_list(Config) ->
Result.
make_del_dir(Config, Handle, Suffix) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -269,7 +266,13 @@ make_del_dir(Config, Handle, Suffix) ->
% because there are processes having that directory as current.
?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]),
?line {ok, CurrentDir} = ?PRIM_FILE_call(get_cwd, Handle, []),
- ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
+ case {os:type(), length(NewDir) >= 260 } of
+ {{win32,_}, true} ->
+ io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH)\n", []),
+ io:format("\nNewDir = ~p\n", [NewDir]);
+ _ ->
+ ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir])
+ end,
try
%% Check that we get an error when trying to create...
%% a deep directory
@@ -302,9 +305,7 @@ make_del_dir(Config, Handle, Suffix) ->
{error, einval} -> ok %FreeBSD
end,
?line {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [""]),
- ?line {error, badarg} = ?PRIM_FILE_call(del_dir, Handle, [[3,2,1,{}]]),
-
- ?line test_server:timetrap_cancel(Dog)
+ ?line {error, badarg} = ?PRIM_FILE_call(del_dir, Handle, [[3,2,1,{}]])
after
?line ok = ?PRIM_FILE_call(set_cwd, Handle, [CurrentDir])
end,
@@ -324,7 +325,6 @@ cur_dir_0b(Config) when is_list(Config) ->
Result.
cur_dir_0(Config, Handle) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
%% Find out the current dir, and cd to it ;-)
?line {ok,BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []),
?line Dir1 = BaseDir ++ "", %% Check that it's a string
@@ -341,31 +341,37 @@ cur_dir_0(Config, Handle) ->
?line RootDir = ?config(priv_dir,Config),
?line NewDir = filename:join(RootDir, DirName),
?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]),
- ?line io:format("cd to ~s",[NewDir]),
- ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
-
- %% Create a file in the new current directory, and check that it
- %% really is created there
- ?line UncommonName = "uncommon.fil",
- ?line {ok,Fd} = ?PRIM_FILE:open(UncommonName, [read, write]),
- ?line ok = ?PRIM_FILE:close(Fd),
- ?line {ok,NewDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
- ?line true = lists:member(UncommonName,NewDirFiles),
-
- %% Delete the directory and return to the old current directory
- %% and check that the created file isn't there (too!)
- ?line expect({error, einval}, {error, eacces}, {error, eexist},
+ case {os:type(), length(NewDir) >= 260} of
+ {{win32,_}, true} ->
+ io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH):\n"),
+ io:format("\nNewDir = ~p\n", [NewDir]);
+ _ ->
+ io:format("cd to ~s",[NewDir]),
+ ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
+
+ %% Create a file in the new current directory, and check that it
+ %% really is created there
+ UncommonName = "uncommon.fil",
+ {ok,Fd} = ?PRIM_FILE:open(UncommonName, [read, write]),
+ ok = ?PRIM_FILE:close(Fd),
+ {ok,NewDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
+ true = lists:member(UncommonName,NewDirFiles),
+
+ %% Delete the directory and return to the old current directory
+ %% and check that the created file isn't there (too!)
+ expect({error, einval}, {error, eacces}, {error, eexist},
?PRIM_FILE_call(del_dir, Handle, [NewDir])),
- ?line ?PRIM_FILE_call(delete, Handle, [UncommonName]),
- ?line {ok,[]} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
- ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]),
- ?line io:format("cd back to ~s",[Dir1]),
- ?line ok = ?PRIM_FILE_call(del_dir, Handle, [NewDir]),
- ?line {error, enoent} = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
- ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]),
- ?line io:format("cd back to ~s",[Dir1]),
- ?line {ok,OldDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
- ?line false = lists:member(UncommonName,OldDirFiles),
+ ?PRIM_FILE_call(delete, Handle, [UncommonName]),
+ {ok,[]} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
+ ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]),
+ io:format("cd back to ~s",[Dir1]),
+ ok = ?PRIM_FILE_call(del_dir, Handle, [NewDir]),
+ {error, enoent} = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]),
+ ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]),
+ io:format("cd back to ~s",[Dir1]),
+ {ok,OldDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]),
+ false = lists:member(UncommonName,OldDirFiles)
+ end,
%% Try doing some bad things
?line {error, badarg} =
@@ -385,7 +391,6 @@ cur_dir_0(Config, Handle) ->
?line {ok, BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []),
?line false = lists:member($\\, BaseDir),
- ?line test_server:timetrap_cancel(Dog),
ok.
%% Tests ?PRIM_FILE:get_cwd/1.
@@ -404,8 +409,6 @@ cur_dir_1b(Config) when is_list(Config) ->
Result.
cur_dir_1(Config, Handle) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
-
?line case os:type() of
{win32, _} ->
win_cur_dir_1(Config, Handle);
@@ -413,7 +416,6 @@ cur_dir_1(Config, Handle) ->
?line {error, enotsup} =
?PRIM_FILE_call(get_cwd, Handle, ["d:"])
end,
- ?line test_server:timetrap_cancel(Dog),
ok.
win_cur_dir_1(_Config, Handle) ->
@@ -439,7 +441,6 @@ win_cur_dir_1(_Config, Handle) ->
open1(suite) -> [];
open1(doc) -> [];
open1(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -465,7 +466,6 @@ open1(Config) when is_list(Config) ->
?line {ok,Fd3} = ?PRIM_FILE:open(Name, [read]),
?line eof = ?PRIM_FILE:read(Fd3,Length),
?line ok = ?PRIM_FILE:close(Fd3),
- ?line test_server:timetrap_cancel(Dog),
ok.
%% Tests all open modes.
@@ -517,7 +517,6 @@ modes(Config) when is_list(Config) ->
close(suite) -> [];
close(doc) -> [];
close(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -534,13 +533,11 @@ close(Config) when is_list(Config) ->
?line Val = ?PRIM_FILE:close(Fd1),
?line io:format("Second close gave: ~p", [Val]),
- ?line test_server:timetrap_cancel(Dog),
ok.
access(suite) -> [];
access(doc) -> [];
access(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -562,7 +559,6 @@ access(Config) when is_list(Config) ->
?line {ok, Str} = ?PRIM_FILE:read(Fd3,length(Str)),
?line ok = ?PRIM_FILE:close(Fd3),
- ?line test_server:timetrap_cancel(Dog),
ok.
%% Tests ?PRIM_FILE:read/2 and ?PRIM_FILE:write/2.
@@ -570,7 +566,6 @@ access(Config) when is_list(Config) ->
read_write(suite) -> [];
read_write(doc) -> [];
read_write(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir, Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -582,7 +577,6 @@ read_write(Config) when is_list(Config) ->
?line {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]),
?line read_write_test(Fd),
- ?line test_server:timetrap_cancel(Dog),
ok.
read_write_test(File) ->
@@ -600,7 +594,6 @@ read_write_test(File) ->
pread_write(suite) -> [];
pread_write(doc) -> [];
pread_write(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir, Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -612,7 +605,6 @@ pread_write(Config) when is_list(Config) ->
?line {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]),
?line pread_write_test(Fd),
- ?line test_server:timetrap_cancel(Dog),
ok.
pread_write_test(File) ->
@@ -632,7 +624,6 @@ pread_write_test(File) ->
append(doc) -> "Test appending to a file.";
append(suite) -> [];
append(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir, Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -659,13 +650,11 @@ append(Config) when is_list(Config) ->
?line Expected = list_to_binary([First, Second, Third]),
?line {ok, Expected} = ?PRIM_FILE:read_file(Name1),
- ?line test_server:timetrap_cancel(Dog),
ok.
exclusive(suite) -> [];
exclusive(doc) -> "Test exclusive access to a file.";
exclusive(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -675,7 +664,6 @@ exclusive(Config) when is_list(Config) ->
?line {ok,Fd} = ?PRIM_FILE:open(Name, [write, exclusive]),
?line {error, eexist} = ?PRIM_FILE:open(Name, [write, exclusive]),
?line ok = ?PRIM_FILE:close(Fd),
- ?line test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -684,7 +672,6 @@ exclusive(Config) when is_list(Config) ->
pos1(suite) -> [];
pos1(doc) -> [];
pos1(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -741,13 +728,11 @@ pos1(Config) when is_list(Config) ->
?line {ok, 0} = ?PRIM_FILE:position(Fd2,{eof,-8}),
?line {ok, "A"} = ?PRIM_FILE:read(Fd2,1),
?line {error, einval} = ?PRIM_FILE:position(Fd2,{eof,-9}),
- ?line test_server:timetrap_cancel(Dog),
ok.
pos2(suite) -> [];
pos2(doc) -> [];
pos2(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -764,7 +749,6 @@ pos2(Config) when is_list(Config) ->
?line {ok, "D"} = ?PRIM_FILE:read(Fd2,1),
?line io:format("DONE"),
- ?line test_server:timetrap_cancel(Dog),
ok.
@@ -782,7 +766,6 @@ file_info_basic_file_b(Config) when is_list(Config) ->
Result.
file_info_basic_file(Config, Handle, Suffix) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir, Config),
%% Create a short file.
@@ -811,7 +794,6 @@ file_info_basic_file(Config, Handle, Suffix) ->
?line {MD, MT} = ModifyTime,
?line all_integers(tuple_to_list(MD) ++ tuple_to_list(MT)),
- ?line test_server:timetrap_cancel(Dog),
ok.
file_info_basic_directory_a(suite) -> [];
@@ -828,8 +810,6 @@ file_info_basic_directory_b(Config) when is_list(Config) ->
Result.
file_info_basic_directory(Config, Handle) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
-
%% Note: filename:join/1 removes any trailing slash,
%% which is essential for ?PRIM_FILE:read_file_info/1 to work on
%% platforms such as Windows95.
@@ -849,7 +829,7 @@ file_info_basic_directory(Config, Handle) ->
_ ->
?line test_directory("/", read, Handle)
end,
- ?line test_server:timetrap_cancel(Dog).
+ ok.
test_directory(Name, ExpectedAccess, Handle) ->
?line {ok, FileInfo} = ?PRIM_FILE_call(read_file_info, Handle, [Name]),
@@ -890,14 +870,12 @@ file_info_bad_b(Config) when is_list(Config) ->
Result.
file_info_bad(Config, Handle) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = filename:join([?config(priv_dir, Config)]),
?line {error, enoent} =
?PRIM_FILE_call(
read_file_info, Handle,
[filename:join(RootDir,
atom_to_list(?MODULE)++"_nonexistent")]),
- ?line test_server:timetrap_cancel(Dog),
ok.
%% Test that the file times behave as they should.
@@ -1192,7 +1170,6 @@ get_good_directory(Config) ->
truncate(suite) -> [];
truncate(doc) -> [];
truncate(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -1218,14 +1195,12 @@ truncate(Config) when is_list(Config) ->
?line {ok, 5} = ?PRIM_FILE:position(Fd2, 5),
?line {error, _} = ?PRIM_FILE:truncate(Fd2),
- ?line test_server:timetrap_cancel(Dog),
ok.
datasync(suite) -> [];
datasync(doc) -> "Tests that ?PRIM_FILE:datasync/1 at least doesn't crash.";
datasync(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line PrivDir = ?config(priv_dir, Config),
?line Sync = filename:join(PrivDir,
atom_to_list(?MODULE)
@@ -1236,14 +1211,12 @@ datasync(Config) when is_list(Config) ->
?line ok = ?PRIM_FILE:datasync(Fd),
?line ok = ?PRIM_FILE:close(Fd),
- ?line test_server:timetrap_cancel(Dog),
ok.
sync(suite) -> [];
sync(doc) -> "Tests that ?PRIM_FILE:sync/1 at least doesn't crash.";
sync(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line PrivDir = ?config(priv_dir, Config),
?line Sync = filename:join(PrivDir,
atom_to_list(?MODULE)
@@ -1254,14 +1227,12 @@ sync(Config) when is_list(Config) ->
?line ok = ?PRIM_FILE:sync(Fd),
?line ok = ?PRIM_FILE:close(Fd),
- ?line test_server:timetrap_cancel(Dog),
ok.
advise(suite) -> [];
advise(doc) -> "Tests that ?PRIM_FILE:advise/4 at least doesn't crash.";
advise(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line PrivDir = ?config(priv_dir, Config),
?line Advise = filename:join(PrivDir,
atom_to_list(?MODULE)
@@ -1325,7 +1296,6 @@ advise(Config) when is_list(Config) ->
?line eof = ?PRIM_FILE:read_line(Fd9),
?line ok = ?PRIM_FILE:close(Fd9),
- ?line test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1369,7 +1339,6 @@ check_large_write(Dog, Fd, _, _, []) ->
allocate(suite) -> [];
allocate(doc) -> "Tests that ?PRIM_FILE:allocate/3 at least doesn't crash.";
allocate(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line PrivDir = ?config(priv_dir, Config),
?line Allocate = filename:join(PrivDir,
atom_to_list(?MODULE)
@@ -1402,7 +1371,6 @@ allocate(Config) when is_list(Config) ->
?line ok = ?PRIM_FILE:write(Fd4, Line2),
?line ok = ?PRIM_FILE:close(Fd4),
- ?line test_server:timetrap_cancel(Dog),
ok.
allocate_and_assert(Fd, Offset, Length) ->
@@ -1450,7 +1418,6 @@ delete_b(Config) when is_list(Config) ->
Result.
delete(Config, Handle, Suffix) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line Name = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -1466,7 +1433,6 @@ delete(Config, Handle, Suffix) ->
?line {error, _} = ?PRIM_FILE:open(Name, [read]),
%% Try deleting a nonexistent file
?line {error, enoent} = ?PRIM_FILE_call(delete, Handle, [Name]),
- ?line test_server:timetrap_cancel(Dog),
ok.
rename_a(suite) ->[];
@@ -1483,7 +1449,6 @@ rename_b(Config) when is_list(Config) ->
Result.
rename(Config, Handle, Suffix) ->
- ?line Dog = test_server:timetrap(test_server:seconds(5)),
?line RootDir = ?config(priv_dir,Config),
?line FileName1 = atom_to_list(?MODULE)++"_rename"++Suffix++".fil",
?line FileName2 = atom_to_list(?MODULE)++"_rename"++Suffix++".ful",
@@ -1536,7 +1501,6 @@ rename(Config, Handle, Suffix) ->
?PRIM_FILE_call(rename, Handle, [DirName2, Name2foo]),
?line io:format("Errmsg2: ~p",[Msg2]),
- ?line test_server:timetrap_cancel(Dog),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2029,6 +1993,9 @@ symlinks(Config, Handle, Suffix) ->
case ?PRIM_FILE_call(make_symlink, Handle, [Name, Alias]) of
{error, enotsup} ->
{skipped, "Links not supported on this platform"};
+ {error, eperm} ->
+ {win32,_} = os:type(),
+ {skipped, "Windows user not privileged to create links"};
ok ->
?line {ok, Info1} =
?PRIM_FILE_call(read_file_info, Handle, [Name]),
diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl
index 2c741232c4..123e849ccb 100644
--- a/lib/kernel/test/sendfile_SUITE.erl
+++ b/lib/kernel/test/sendfile_SUITE.erl
@@ -72,7 +72,12 @@ end_per_suite(Config) ->
file:delete(proplists:get_value(big_file, Config)).
init_per_group(async_threads,Config) ->
- [{sendfile_opts,[{use_threads,true}]}|Config];
+ case erlang:system_info(thread_pool_size) of
+ 0 ->
+ {skip,"No async threads"};
+ _ ->
+ [{sendfile_opts,[{use_threads,true}]}|Config]
+ end;
init_per_group(no_async_threads,Config) ->
[{sendfile_opts,[{use_threads,false}]}|Config].
diff --git a/lib/megaco/aclocal.m4 b/lib/megaco/aclocal.m4
index c51c26794a..ed492d55ff 100644
--- a/lib/megaco/aclocal.m4
+++ b/lib/megaco/aclocal.m4
@@ -83,8 +83,10 @@ AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross
AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_LM_CONF, [OSE load module default configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE OSE confd source file])
+AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file])
AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file])
])
@@ -1116,7 +1118,7 @@ case "$THR_LIB_NAME" in
[Define if you have the "ose_spi/ose_spi.h" header file.]))
;;
esac
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
case $host_os in
openbsd*)
# The default stack size is insufficient for our needs
@@ -1220,7 +1222,7 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for functions
dnl
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
AC_CHECK_FUNC(pthread_spin_lock, \
[ethr_have_native_spinlock=yes \
AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml
index f71166b1b1..dff36fd51c 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -36,7 +36,23 @@
section is the version number of Megaco.</p>
- <section><title>Megaco 3.17.0.3</title>
+ <section><title>Megaco 3.17.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The default encoding of Erlang files has been changed
+ from ISO-8859-1 to UTF-8. </p> <p> The encoding of XML
+ files has also been changed to UTF-8. </p>
+ <p>
+ Own Id: OTP-10907</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Megaco 3.17.0.3</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/megaco/src/app/megaco.app.src b/lib/megaco/src/app/megaco.app.src
index 40265166ae..6ab85a1bbc 100644
--- a/lib/megaco/src/app/megaco.app.src
+++ b/lib/megaco/src/app/megaco.app.src
@@ -112,7 +112,10 @@
megaco_trans_sup, megaco_misc_sup, megaco_sup]},
{applications, [stdlib, kernel]},
{env, []},
- {mod, {megaco_sup, []}}
+ {mod, {megaco_sup, []}},
+ {runtime_dependencies, ["stdlib-2.0","runtime_tools-1.8.14","kernel-3.0",
+ "et-1.5","erts-6.0","debugger-4.0",
+ "asn1-3.0"]}
]}.
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index 01d429d0ae..373f5199bf 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.17.0.3
+MEGACO_VSN = 3.17.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 0e9131190d..213c2b6d21 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -38,7 +38,69 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.11</title>
+ <section><title>Mnesia 4.12</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Some local implementations of removing the last element
+ from a list are replaced by <c>lists:droplast/1</c>. Note
+ that this requires at least <c>stdlib-2.0</c>, which is
+ the stdlib version delivered in OTP 17.0. (Thanks to Hans
+ Svensson)</p>
+ <p>
+ Own Id: OTP-11678</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ To prevent a race condition if there is a short
+ communication problem when node-down and node-up events
+ are received. They are now stored and later checked if
+ the node came up just before mnesia flagged the node as
+ down. (Thanks to Jonas Falkevik )</p>
+ <p>
+ Own Id: OTP-11497</p>
+ </item>
+ <item>
+ <p>
+ Added <c>mnesia:sync_log/0</c> to explicit sync mnesias
+ transaction log.</p>
+ <p>
+ Own Id: OTP-11729</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.11</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/mnesia/src/mnesia.app.src b/lib/mnesia/src/mnesia.app.src
index 3715488ec2..e755864792 100644
--- a/lib/mnesia/src/mnesia.app.src
+++ b/lib/mnesia/src/mnesia.app.src
@@ -47,6 +47,7 @@
mnesia_tm
]},
{applications, [kernel, stdlib]},
- {mod, {mnesia_sup, []}}]}.
+ {mod, {mnesia_sup, []}},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/mnesia/src/mnesia.hrl b/lib/mnesia/src/mnesia.hrl
index 2855792646..c8010d5466 100644
--- a/lib/mnesia/src/mnesia.hrl
+++ b/lib/mnesia/src/mnesia.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,7 +35,7 @@
-define(ets_last(Tab), ets:last(Tab)).
-define(ets_prev(Tab, Key), ets:prev(Tab, Key)).
-define(ets_slot(Tab, Pos), ets:slot(Tab, Pos)).
--define(ets_new_table(Tab, Props), ets:new(Tab, Props)).
+-define(ets_new_table(Tab, Props), _ = ets:new(Tab, Props)).
-define(ets_delete_table(Tab), ets:delete(Tab)).
-define(ets_fixtable(Tab, Bool), ets:fixtable(Tab, Bool)).
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index a83e55ac62..fe2fd67d71 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -1208,7 +1208,14 @@ handle_info(Done = #loader_done{worker_pid=WPid, table_name=Tab}, State0) ->
{value,{_,Worker}} = lists:keysearch(WPid,1,get_loaders(State0)),
add_loader(Tab,Worker,State1);
_ ->
- State1
+ DelState = State1#state{late_loader_queue=gb_trees:delete_any(Tab, LateQueue0)},
+ case ?catch_val({Tab, storage_type}) of
+ ram_copies ->
+ cast({disc_load, Tab, ram_only}),
+ DelState;
+ _ ->
+ DelState
+ end
end
end,
State3 = opt_start_worker(State2),
diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl
index 8fef611a48..87cb58dae1 100644
--- a/lib/mnesia/src/mnesia_index.erl
+++ b/lib/mnesia/src/mnesia_index.erl
@@ -52,7 +52,11 @@
val(Var) ->
case ?catch_val(Var) of
- {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
+ {'EXIT', _ReASoN_} ->
+ case mnesia_lib:other_val(Var) of
+ error -> mnesia_lib:pr_other(Var, _ReASoN_);
+ Val -> Val
+ end;
_VaLuE_ -> _VaLuE_
end.
diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl
index 109e924971..a32c69c59e 100644
--- a/lib/mnesia/src/mnesia_lib.erl
+++ b/lib/mnesia/src/mnesia_lib.erl
@@ -115,6 +115,8 @@
mkcore/1,
not_active_here/1,
other_val/2,
+ other_val/1,
+ pr_other/2,
overload_read/0,
overload_read/1,
overload_set/2,
@@ -389,16 +391,19 @@ unset(Var) ->
?ets_delete(mnesia_gvar, Var).
other_val(Var, Other) ->
+ case other_val(Var) of
+ error -> pr_other(Var, Other);
+ Val -> Val
+ end.
+
+other_val(Var) ->
case Var of
{_, where_to_read} -> nowhere;
{_, where_to_write} -> [];
{_, active_replicas} -> [];
- _ ->
- pr_other(Var, Other)
+ _ -> error
end.
--spec pr_other(_,_) -> no_return().
-
pr_other(Var, Other) ->
Why =
case is_running() of
diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl
index 32cea903c9..c3846b00c0 100644
--- a/lib/mnesia/src/mnesia_locker.erl
+++ b/lib/mnesia/src/mnesia_locker.erl
@@ -131,9 +131,14 @@ send_release_tid(Nodes, Tid) ->
receive_release_tid_acc([Node | Nodes], Tid) ->
receive
{?MODULE, Node, {tid_released, Tid}} ->
- receive_release_tid_acc(Nodes, Tid);
- {mnesia_down, Node} ->
receive_release_tid_acc(Nodes, Tid)
+ after 0 ->
+ receive
+ {?MODULE, Node, {tid_released, Tid}} ->
+ receive_release_tid_acc(Nodes, Tid);
+ {mnesia_down, Node} ->
+ receive_release_tid_acc(Nodes, Tid)
+ end
end;
receive_release_tid_acc([], _Tid) ->
ok.
@@ -270,7 +275,8 @@ try_sticky_lock(Tid, Op, Pid, {Tab, _} = Oid) ->
try_lock(Tid, Op, Pid, Oid);
[{_,N}] ->
Req = {Pid, {Op, Tid, Oid}},
- Pid ! {?MODULE, node(), {switch, N, Req}}
+ Pid ! {?MODULE, node(), {switch, N, Req}},
+ true
end.
try_lock(Tid, read_write, Pid, Oid) ->
diff --git a/lib/mnesia/src/mnesia_recover.erl b/lib/mnesia/src/mnesia_recover.erl
index 0548a25ebf..b6492707e2 100644
--- a/lib/mnesia/src/mnesia_recover.erl
+++ b/lib/mnesia/src/mnesia_recover.erl
@@ -178,7 +178,11 @@ log_decision(D) ->
val(Var) ->
case ?catch_val(Var) of
- {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
+ {'EXIT', Reason} ->
+ case mnesia_lib:other_val(Var) of
+ error -> mnesia_lib:pr_other(Var, Reason);
+ Val -> Val
+ end;
Value -> Value
end.
diff --git a/lib/mnesia/src/mnesia_snmp_hook.erl b/lib/mnesia/src/mnesia_snmp_hook.erl
index 893b39f3c0..256f83b029 100644
--- a/lib/mnesia/src/mnesia_snmp_hook.erl
+++ b/lib/mnesia/src/mnesia_snmp_hook.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,7 +32,11 @@
val(Var) ->
case ?catch_val(Var) of
- {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
+ {'EXIT', _ReASoN_} ->
+ case mnesia_lib:other_val(Var) of
+ error -> mnesia_lib:pr_other(Var, _ReASoN_);
+ Val -> Val
+ end;
_VaLuE_ -> _VaLuE_
end.
diff --git a/lib/mnesia/test/mnesia_config_test.erl b/lib/mnesia/test/mnesia_config_test.erl
index 6baf86a4a5..c495bce63f 100644
--- a/lib/mnesia/test/mnesia_config_test.erl
+++ b/lib/mnesia/test/mnesia_config_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -639,10 +639,10 @@ send_compressed(Config) ->
end,
?match([], mnesia_test_lib:kill_mnesia([N2])),
-
+ sys:get_status(mnesia_monitor), %% sync N1
?match([], mnesia_test_lib:kill_mnesia([N1])),
?match(ok, mnesia:start([{send_compressed, 9}])),
- ?match(ok, mnesia:wait_for_tables([t0,t1,t2], 5000)),
+ ?match(ok, mnesia:wait_for_tables([t0,t1,t2], 25000)),
?match({atomic, ok}, mnesia:transaction(Create, [t0])),
?match({atomic, ok}, mnesia:transaction(Create, [t1])),
@@ -1158,6 +1158,7 @@ dynamic_basic(Config) when is_list(Config) ->
%%% SYNC!!!
timer:sleep(1000),
+ sys:get_status(mnesia_monitor),
?match([N3,N1], sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
?match([N3,N1], sort(rpc:call(N3, mnesia, system_info, [running_db_nodes]))),
diff --git a/lib/mnesia/test/mnesia_evil_coverage_test.erl b/lib/mnesia/test/mnesia_evil_coverage_test.erl
index 91820238e5..2d1623b6ca 100644
--- a/lib/mnesia/test/mnesia_evil_coverage_test.erl
+++ b/lib/mnesia/test/mnesia_evil_coverage_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -671,13 +671,16 @@ add_copy_when_going_down(Config) ->
?acquire_nodes(2, Config ++ [{tc_timeout, timer:minutes(2)}]),
?match({atomic, ok}, mnesia:create_table(a, [{ram_copies, [Node1]}])),
%% Grab a write lock
+ Tester = self(),
WriteAndWait = fun() ->
mnesia:write({a,1,1}),
- receive continue -> ok
+ Tester ! {self(), got_lock},
+ receive continue -> ok
end
end,
- _Lock = spawn(fun() -> mnesia:transaction(WriteAndWait) end),
- Tester = self(),
+ Locker = spawn(fun() -> mnesia:transaction(WriteAndWait) end),
+ receive {Locker, got_lock} -> ok end,
+
spawn_link(fun() -> Res = rpc:call(Node2, mnesia, add_table_copy,
[a, Node2, ram_copies]),
Tester ! {test, Res}
diff --git a/lib/mnesia/test/mnesia_qlc_test.erl b/lib/mnesia/test/mnesia_qlc_test.erl
index 5f46840ae9..9886754710 100644
--- a/lib/mnesia/test/mnesia_qlc_test.erl
+++ b/lib/mnesia/test/mnesia_qlc_test.erl
@@ -264,7 +264,7 @@ atomic_eval(Config) ->
?match({1,[{a,{a,9},91}]}, ok(Restart,[Pid3, Cursor])),
QC1 = ok(fun() -> qlc:cursor(Q1) end, []),
- ?match({'EXIT', _}, qlc:next_answers(QC1)),
+ ?match({'EXIT', _}, (catch qlc:next_answers(QC1))),
?match({aborted,_}, ok(fun()->qlc:next_answers(QC1)end,[])),
?verify_mnesia(Ns, []).
diff --git a/lib/mnesia/test/mnesia_test_lib.hrl b/lib/mnesia/test/mnesia_test_lib.hrl
index 281634c239..94a195f01f 100644
--- a/lib/mnesia/test/mnesia_test_lib.hrl
+++ b/lib/mnesia/test/mnesia_test_lib.hrl
@@ -46,15 +46,32 @@
-define(match(ExpectedRes,Expr),
fun() ->
- AcTuAlReS = (catch (Expr)),
- case AcTuAlReS of
- ExpectedRes ->
- ?verbose("ok, ~n Result as expected:~p~n",[AcTuAlReS]),
- {success,AcTuAlReS};
- _ ->
- ?error("Not Matching Actual result was:~n ~p~n",
- [AcTuAlReS]),
- {fail,AcTuAlReS}
+ try Expr of
+ _AR_0 = ExpectedRes ->
+ ?verbose("ok, ~n Result as expected:~p~n",[_AR_0]),
+ {success,_AR_0};
+ _AR_0 ->
+ ?error("Not Matching Actual result was:~n ~p~n",[_AR_0]),
+ {fail,_AR_0}
+ catch
+ exit:{aborted, _ER_1} when
+ element(1, _ER_1) =:= node_not_running;
+ element(1, _ER_1) =:= bad_commit;
+ element(1, _ER_1) =:= cyclic ->
+ %% Need to re-raise these to restart transaction
+ erlang:raise(exit, {aborted, _ER_1}, erlang:get_stacktrace());
+ exit:_AR_1 ->
+ case fun(_AR_EXIT_) -> {'EXIT', _AR_EXIT_} end(_AR_1) of
+ _AR_2 = ExpectedRes ->
+ ?verbose("ok, ~n Result as expected:~p~n",[_AR_2]),
+ {success,_AR_2};
+ _AR_2 ->
+ ?error("Not Matching Actual result was:~n ~p~n", [_AR_2]),
+ {fail,_AR_2}
+ end;
+ _:_AR_1 ->
+ ?error("Not Matching Actual result was:~n ~p~n", [_AR_1]),
+ {fail,_AR_1}
end
end()).
diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl
index 157e441b27..237984978e 100644
--- a/lib/mnesia/test/mnesia_trans_access_test.erl
+++ b/lib/mnesia/test/mnesia_trans_access_test.erl
@@ -677,7 +677,7 @@ check_res(sync_dirty, Res) when is_list(Res) ->
check_res(ets, Res) when is_list(Res) ->
Res;
check_res(Type,Res) ->
- ?match(bug,{Type,Res}).
+ ?match({bug, bug},{Type,Res}).
read_op(Oid) ->
case lists:reverse(mnesia:read(Oid)) of
@@ -1118,10 +1118,7 @@ create_live_table_index(Config, Storage) ->
ValPos = 3,
mnesia:dirty_write({Tab, 1, 2}),
- Fun = fun() ->
- ?match(ok, mnesia:write({Tab, 2, 2})),
- ok
- end,
+ Fun = fun() -> mnesia:write({Tab, 2, 2}) end,
?match({atomic, ok}, mnesia:transaction(Fun)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
IRead = fun() -> lists:sort(mnesia:index_read(Tab, 2, ValPos)) end,
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index 064ba43791..c596f98c81 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.11
+MNESIA_VSN = 4.12
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index 9de00b8c16..a2c5eda9d7 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -31,6 +31,68 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 2.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ etop trace handler now works in smp environment (Thanks
+ to Péter Gömöri)</p>
+ <p>
+ Own Id: OTP-11633</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Removed gs based applications and gs based backends. The
+ <c>observer</c> application replaces the removed
+ applications.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10915</p>
+ </item>
+ <item>
+ <p>
+ The <c>crashdump_viewer</c> is re-written using
+ <c>wx</c>. The old <c>webtool</c> interface for
+ <c>crashdump_viewer</c> does no longer exist.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11179</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 1.3.1.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/observer/src/cdv_dist_cb.erl b/lib/observer/src/cdv_dist_cb.erl
index 3860324d6f..f7e6c9aded 100644
--- a/lib/observer/src/cdv_dist_cb.erl
+++ b/lib/observer/src/cdv_dist_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,8 @@
get_info/1,
get_detail_cols/1,
get_details/1,
- detail_pages/0]).
+ detail_pages/0,
+ format/1]).
-include_lib("wx/include/wx.hrl").
-include("crashdump_viewer.hrl").
@@ -75,6 +76,11 @@ init_gen_page(Parent, Info) ->
Fields = info_fields(),
cdv_info_wx:start_link(Parent,{Fields,Info,[]}).
+format({creations,Creations}) ->
+ string:join([integer_to_list(C) || C <- Creations],",");
+format(D) ->
+ D.
+
%%%-----------------------------------------------------------------
%%% Internal
info_fields() ->
@@ -83,7 +89,7 @@ info_fields() ->
{"Type", conn_type},
{"Channel", channel},
{"Controller", {click,controller}},
- {"Creation", creation},
+ {"Creation", {{format,fun format/1},creation}},
{"Extra Info", error}]},
{scroll_boxes,
[{"Remote Links",1,{click,remote_links}},
diff --git a/lib/observer/src/cdv_timer_cb.erl b/lib/observer/src/cdv_timer_cb.erl
index 9cdbfa05a9..d44592cf18 100644
--- a/lib/observer/src/cdv_timer_cb.erl
+++ b/lib/observer/src/cdv_timer_cb.erl
@@ -27,18 +27,21 @@
%% Defines
-define(COL_OWNER, 0).
--define(COL_MSG, ?COL_OWNER+1).
+-define(COL_NAME, ?COL_OWNER+1).
+-define(COL_MSG, ?COL_NAME+1).
-define(COL_TIME, ?COL_MSG+1).
%% Callbacks for cdv_virtual_list_wx
col_to_elem(id) -> col_to_elem(?COL_OWNER);
col_to_elem(?COL_OWNER) -> #timer.pid;
+col_to_elem(?COL_NAME) -> #timer.name;
col_to_elem(?COL_MSG) -> #timer.msg;
col_to_elem(?COL_TIME) -> #timer.time.
col_spec() ->
[{"Owner", ?wxLIST_FORMAT_LEFT, 110},
- {"Message", ?wxLIST_FORMAT_LEFT, 400},
+ {"Owner name", ?wxLIST_FORMAT_LEFT, 150},
+ {"Message", ?wxLIST_FORMAT_LEFT, 300},
{"Time left (ms)", ?wxLIST_FORMAT_RIGHT, 80}].
get_info(Owner) ->
diff --git a/lib/observer/src/cdv_virtual_list_wx.erl b/lib/observer/src/cdv_virtual_list_wx.erl
index c5a7d9a2e5..bfe115a42e 100644
--- a/lib/observer/src/cdv_virtual_list_wx.erl
+++ b/lib/observer/src/cdv_virtual_list_wx.erl
@@ -269,7 +269,7 @@ handle_event(#wx{event=#wxList{type=command_list_item_right_click,
MenuId = ?ID_DETAILS + Col,
ColText = call(Holder, {get_row, self(), Row, Col}),
case ColText of
- "[]" -> [];
+ Empty when Empty=="[]"; Empty=="" -> [];
_ ->
What =
case catch list_to_integer(ColText) of
@@ -284,8 +284,13 @@ handle_event(#wx{event=#wxList{type=command_list_item_right_click,
end
end,
MenuCols),
- wxWindow:popupMenu(Panel, Menu),
- wxMenu:destroy(Menu),
+ case MenuItems of
+ [] ->
+ wxMenu:destroy(Menu);
+ _ ->
+ wxWindow:popupMenu(Panel, Menu),
+ wxMenu:destroy(Menu)
+ end,
{noreply,State#state{menu_items=MenuItems}};
handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}},
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index a17efbccb0..99329b94e2 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -298,6 +298,7 @@ expand_binary(Pos) ->
%%--------------------------------------------------------------------
init([]) ->
ets:new(cdv_dump_index_table,[ordered_set,named_table,public]),
+ ets:new(cdv_reg_proc_table,[ordered_set,named_table,public]),
{ok, #state{}}.
%%--------------------------------------------------------------------
@@ -978,9 +979,20 @@ count() ->
%%-----------------------------------------------------------------
%% Page with all processes
procs_summary(File,WS) ->
- ParseFun = fun(Fd,Pid) ->
+ ParseFun = fun(Fd,Pid0) ->
+ Pid = list_to_pid(Pid0),
Proc = get_procinfo(Fd,fun main_procinfo/5,
- #proc{pid=list_to_pid(Pid)},WS),
+ #proc{pid=Pid},WS),
+ case Proc#proc.name of
+ undefined ->
+ true;
+ Name ->
+ %% Registered process - store to allow
+ %% lookup for timers connected to
+ %% registered name instead of pid.
+ ets:insert(cdv_reg_proc_table,{Name,Pid}),
+ ets:insert(cdv_reg_proc_table,{Pid0,Name})
+ end,
case Proc#proc.memory of
undefined -> Proc#proc{memory=Proc#proc.stack_heap};
_ -> Proc
@@ -1495,8 +1507,28 @@ get_internal_ets_tables(File,WS) ->
%%-----------------------------------------------------------------
%% Page with list of all timers
get_timers(File,Pid) ->
- ParseFun = fun(Fd,Id) -> get_timerinfo_1(Fd,#timer{pid=list_to_pid(Id)}) end,
- lookup_and_parse_index(File,{?timer,Pid},ParseFun,"timers").
+ ParseFun = fun(Fd,Id) -> get_timerinfo(Fd,Id) end,
+ T1 = lookup_and_parse_index(File,{?timer,Pid},ParseFun,"timers"),
+ T2 = case ets:lookup(cdv_reg_proc_table,Pid) of
+ [{_,Name}] ->
+ lookup_and_parse_index(File,{?timer,Name},ParseFun,"timers");
+ _ ->
+ []
+ end,
+ T1 ++ T2.
+
+get_timerinfo(Fd,Id) ->
+ case catch list_to_pid(Id) of
+ Pid when is_pid(Pid) ->
+ get_timerinfo_1(Fd,#timer{pid=Pid});
+ _ ->
+ case ets:lookup(cdv_reg_proc_table,Id) of
+ [{_,Pid}] when is_pid(Pid) ->
+ get_timerinfo_1(Fd,#timer{pid=Pid,name=Id});
+ [] ->
+ get_timerinfo_1(Fd,#timer{name=Id})
+ end
+ end.
get_timerinfo_1(Fd,Timer) ->
case line_head(Fd) of
@@ -1576,7 +1608,13 @@ get_nodeinfo(Fd,Nod) ->
"Controller" ->
get_nodeinfo(Fd,Nod#nod{controller=val(Fd)});
"Creation" ->
- get_nodeinfo(Fd,Nod#nod{creation=list_to_integer(val(Fd))});
+ %% Throwing away elements like "(refc=1)", which might be
+ %% printed from a debug compiled emulator.
+ Creations = lists:flatmap(fun(C) -> try [list_to_integer(C)]
+ catch error:badarg -> []
+ end
+ end, string:tokens(val(Fd)," ")),
+ get_nodeinfo(Fd,Nod#nod{creation={creations,Creations}});
"Remote link" ->
Procs = val(Fd), % e.g. "<0.31.0> <4322.54.0>"
{Local,Remote} = split(Procs),
@@ -2559,11 +2597,11 @@ progress_pmap(Report,File,Fun,List) ->
{L1,L2} = if length(L)>=NPerProc -> lists:split(NPerProc,L);
true -> {L,[]} % last chunk
end,
- P = spawn(
+ {P,_Ref} =
+ spawn_monitor(
fun() ->
progress_map(Collector,ReportInterval,File,Fun,L1)
end),
- erlang:monitor(process,P),
{L2,[P|Ps]}
end,
{List,[]},
diff --git a/lib/observer/src/crashdump_viewer.hrl b/lib/observer/src/crashdump_viewer.hrl
index ae288ed573..0e2eba6dee 100644
--- a/lib/observer/src/crashdump_viewer.hrl
+++ b/lib/observer/src/crashdump_viewer.hrl
@@ -108,6 +108,7 @@
-record(timer,
{pid,
+ name,
msg,
time}).
diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src
index f14f0ee849..97a54cd6f9 100644
--- a/lib/observer/src/observer.app.src
+++ b/lib/observer/src/observer.app.src
@@ -60,6 +60,9 @@
ttb_et]},
{registered, []},
{applications, [kernel, stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["wx-1.2","stdlib-2.0","runtime_tools-1.8.14",
+ "kernel-3.0","inets-5.10","et-1.5",
+ "erts-6.0"]}]}.
diff --git a/lib/observer/src/observer.erl b/lib/observer/src/observer.erl
index 098100e8ee..a30ceecc63 100644
--- a/lib/observer/src/observer.erl
+++ b/lib/observer/src/observer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,8 +18,11 @@
-module(observer).
--export([start/0]).
+-export([start/0, stop/0]).
start() ->
observer_wx:start().
+
+stop() ->
+ observer_wx:stop().
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index cedaf7d2b8..34c7b127ff 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -249,6 +249,8 @@ to_str({func, {F,A}}) when is_atom(F), is_integer(A) ->
lists:concat([F, "/", A]);
to_str({func, {F,'_'}}) when is_atom(F) ->
atom_to_list(F);
+to_str({{format,Fun},Value}) when is_function(Fun) ->
+ Fun(Value);
to_str({A, B}) when is_atom(A), is_atom(B) ->
lists:concat([A, ":", B]);
to_str({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) ->
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 3ffa5fc77d..8e8a37fc93 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -34,6 +34,7 @@
-record(state, {parent,
frame,
+ notebook,
pid,
pages=[],
expand_table,
@@ -76,6 +77,7 @@ init([Pid, ParentFrame, Parent]) ->
{Frame, #state{parent=Parent,
pid=Pid,
frame=Frame,
+ notebook=Notebook,
pages=[ProcessPage,MessagePage,DictPage,StackPage,StatePage],
expand_table=Table
}}
@@ -158,6 +160,9 @@ handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Info}}}, State)
handle_event(Event, _State) ->
error({unhandled_event, Event}).
+handle_info({get_debug_info, From}, State = #state{notebook=Notebook}) ->
+ From ! {procinfo_debug, Notebook},
+ {noreply, State};
handle_info(_Info, State) ->
%% io:format("~p: ~p, Handle info: ~p~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl
index 59fe5b5670..7757dfea53 100644
--- a/lib/observer/src/observer_tv_table.erl
+++ b/lib/observer/src/observer_tv_table.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -409,7 +409,7 @@ handle_info({refresh, Min, Min}, State = #state{grid=Grid}) ->
wxListCtrl:refreshItem(Grid, Min), %% Avoid assert in wx below if Max is 0
{noreply, State};
handle_info({refresh, Min, Max}, State = #state{grid=Grid}) ->
- wxListCtrl:refreshItems(Grid, Min, Max),
+ Max > 0 andalso wxListCtrl:refreshItems(Grid, Min, Max),
{noreply, State};
handle_info(refresh_interval, State = #state{pid=Pid}) ->
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index b276965f83..da4cb8e041 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -132,8 +132,8 @@ handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) ->
observer_lib:set_listctrl_col_size(Grid, W),
{noreply, State};
-handle_event(#wx{obj=Grid, event=#wxList{type=command_list_item_activated,
- itemIndex=Index}},
+handle_event(#wx{event=#wxList{type=command_list_item_activated,
+ itemIndex=Index}},
State=#state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs}) ->
Table = lists:nth(Index+1, Tabs),
case Table#tab.protection of
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index ecb8e132fe..03ca1bf9c1 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -19,7 +19,7 @@
-behaviour(wx_object).
--export([start/0]).
+-export([start/0, stop/0]).
-export([create_menus/2, get_attrib/1, get_tracer/0, set_status/1,
create_txt_dialog/4, try_rpc/4, return_to_localnode/2]).
@@ -69,6 +69,9 @@ start() ->
_Obj -> ok
end.
+stop() ->
+ wx_object:call(observer, stop).
+
create_menus(Object, Menus) when is_list(Menus) ->
wx_object:call(Object, {create_menus, Menus}).
@@ -109,7 +112,8 @@ setup(#state{frame = Frame} = State) ->
observer_lib:create_menus(DefMenus, MenuBar, default),
wxFrame:setMenuBar(Frame, MenuBar),
- StatusBar = wxFrame:createStatusBar(Frame, []),
+ StatusBar = wxStatusBar:new(Frame),
+ wxFrame:setStatusBar(Frame, StatusBar),
wxFrame:setTitle(Frame, atom_to_list(node())),
wxStatusBar:setStatusText(StatusBar, atom_to_list(node())),
@@ -331,6 +335,10 @@ handle_call({get_attrib, Attrib}, _From, State) ->
handle_call(get_tracer, _From, State=#state{trace_panel=TraceP}) ->
{reply, TraceP, State};
+handle_call(stop, _, State = #state{frame = Frame}) ->
+ wxFrame:destroy(Frame),
+ {stop, normal, ok, State};
+
handle_call(_Msg, _From, State) ->
{reply, ok, State}.
@@ -367,6 +375,10 @@ handle_info({open_link, Pid0}, State = #state{pro_panel=ProcViewer, frame=Frame}
end,
{noreply, State};
+handle_info({get_debug_info, From}, State = #state{notebook=Notebook, active_tab=Pid}) ->
+ From ! {observer_debug, wx:get_env(), Notebook, Pid},
+ {noreply, State};
+
handle_info({'EXIT', Pid, _Reason}, State) ->
io:format("Child (~s) crashed exiting: ~p ~p~n",
[pid2panel(Pid, State), Pid,_Reason]),
@@ -377,6 +389,7 @@ handle_info(_Info, State) ->
terminate(_Reason, #state{frame = Frame}) ->
wxFrame:destroy(Frame),
+ wx:destroy(),
ok.
code_change(_, _, State) ->
diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl
index 40dbe28d46..0eb4a92c53 100644
--- a/lib/observer/test/crashdump_helper.erl
+++ b/lib/observer/test/crashdump_helper.erl
@@ -35,7 +35,9 @@ n1_proc(Creator,_N2,Pid2,Port2,_L) ->
register(aaaaaaaa,self()),
process_flag(save_calls,3),
ets:new(cdv_test_ordset_table,[ordered_set]),
- erlang:send_after(1000000,self(),cdv_test_timer_message),
+ erlang:send_after(1000000,self(),cdv_test_timer_message1),
+ erlang:send_after(1000000,aaaaaaaa,cdv_test_timer_message2),
+ erlang:send_after(1000000,noexistproc,cdv_test_timer_message3),
Port = hd(erlang:ports()),
Fun = fun() -> ok end,
Ref = make_ref(),
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 7a582436b4..03ab0c20e1 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -120,27 +120,63 @@ start_stop(Config) when is_list(Config) ->
Dump = hd(?config(dumps,Config)),
timer:sleep(1000),
- ProcsBefore = length(processes()),
+ ProcsBefore = processes(),
+ NumProcsBefore = length(ProcsBefore),
ok = crashdump_viewer:start(Dump),
- true = is_pid(whereis(crashdump_viewer_server)),
- true = is_pid(whereis(cdv_wx)),
- true = is_pid(whereis(cdv_proc_cb)),
- true = is_pid(whereis(cdv_port_cb)),
- true = is_pid(whereis(cdv_ets_cb)),
- true = is_pid(whereis(cdv_timer_cb)),
- true = is_pid(whereis(cdv_fun_cb)),
- true = is_pid(whereis(cdv_atom_cb)),
- true = is_pid(whereis(cdv_dist_cb)),
- true = is_pid(whereis(cdv_mod_cb)),
+ ExpectedRegistered = [crashdump_viewer_server,
+ cdv_wx,
+ cdv_proc_cb,
+ cdv_proc_cb__holder,
+ cdv_port_cb,
+ cdv_port_cb__holder,
+ cdv_ets_cb,
+ cdv_ets_cb__holder,
+ cdv_timer_cb,
+ cdv_timer_cb__holder,
+ cdv_fun_cb,
+ cdv_fun_cb__holder,
+ cdv_atom_cb,
+ cdv_atom_cb__holder,
+ cdv_dist_cb,
+ cdv_dist_cb__holder,
+ cdv_mod_cb,
+ cdv_mod_cb__holder],
+ Regs=[begin
+ P=whereis(N),
+ {P,N,erlang:monitor(process,P)}
+ end || N <- ExpectedRegistered],
+ ct:log("CDV procs: ~n~p~n",[Regs]),
+ [true=is_pid(P) || {P,_,_} <- Regs],
timer:sleep(5000), % give some time to live
ok = crashdump_viewer:stop(),
- timer:sleep(1000), % give some time to stop
- undefined = whereis(crashdump_viewer_server),
- undefined = whereis(cdv_wx),
- ProcsAfter=length(processes()),
- ProcsAfter=ProcsBefore,
+ recv_downs(Regs),
+ timer:sleep(2000),
+ ProcsAfter = processes(),
+ NumProcsAfter = length(ProcsAfter),
+ if NumProcsAfter=/=NumProcsBefore ->
+ ct:log("Before but not after:~n~p~n",
+ [[{P,process_info(P)} || P <- ProcsBefore -- ProcsAfter]]),
+ ct:log("After but not before:~n~p~n",
+ [[{P,process_info(P)} || P <- ProcsAfter -- ProcsBefore]]),
+ ct:fail("leaking processes");
+ true ->
+ ok
+ end,
ok.
+recv_downs([]) ->
+ ok;
+recv_downs(Regs) ->
+ receive
+ {'DOWN',Ref,process,_Pid,_} ->
+ ct:log("Got 'DOWN' for process ~n~p~n",[_Pid]),
+ recv_downs(lists:keydelete(Ref,3,Regs))
+ after 30000 ->
+ ct:log("Timeout waiting for down:~n~p~n",
+ [[{Reg,process_info(P)} || {P,_,_}=Reg <- Regs]]),
+ ct:log("Message queue:~n~p~n",[process_info(self(),messages)])
+ end.
+
%% Try to load nonexisting file
non_existing(Config) when is_list(Config) ->
ExpectedReason = "non-existing-file is not an Erlang crash dump\n",
@@ -265,10 +301,12 @@ wait_for_progress_done() ->
%%%-----------------------------------------------------------------
%%% General check of what is displayed for a dump
browse_file(File) ->
- io:format("Browsing file: ~s~n",[File]),
+ io:format("~nBrowsing file: ~s",[File]),
ok = start_backend(File),
+ io:format(" backend started",[]),
+
{ok,_GI=#general_info{},_GenTW} = crashdump_viewer:general_info(),
{ok,Procs,_ProcsTW} = crashdump_viewer:processes(),
{ok,Ports,_PortsTW} = crashdump_viewer:ports(),
@@ -285,10 +323,16 @@ browse_file(File) ->
{ok,_HashTabs,_HashTabsTW} = crashdump_viewer:hash_tables(),
{ok,_IndexTabs,_IndexTabsTW} = crashdump_viewer:index_tables(),
+ io:format(" info read",[]),
+
lookat_all_pids(Procs),
+ io:format(" pids ok",[]),
lookat_all_ports(Ports),
+ io:format(" ports ok",[]),
lookat_all_mods(Mods),
+ io:format(" mods ok",[]),
lookat_all_nodes(Nodes),
+ io:format(" nodes ok",[]),
Procs. % used as second arg to special/2
@@ -303,6 +347,7 @@ special(File,Procs) ->
[#proc{pid=Pid0}|_Rest] = lists:keysort(#proc.name,Procs),
Pid = pid_to_list(Pid0),
{ok,ProcDetails=#proc{},[]} = crashdump_viewer:proc_details(Pid),
+ io:format(" process details ok",[]),
#proc{dict=Dict} = ProcDetails,
@@ -314,6 +359,7 @@ special(File,Procs) ->
['#CDVBin',SOffset,SSize,SPos] = proplists:get_value(sub_bin,Dict),
{ok,<<_:SSize/binary>>} =
crashdump_viewer:expand_binary({SOffset,SSize,SPos}),
+ io:format(" expand binary ok",[]),
['#CDVPid',X1,Y1,Z1] = proplists:get_value(ext_pid,Dict),
ChannelStr1 = integer_to_list(X1),
@@ -323,22 +369,34 @@ special(File,Procs) ->
integer_to_list(Z1) ++ ">",
{error,{other_node,ChannelStr1}} =
crashdump_viewer:proc_details(ExtPid),
+ io:format(" process details external ok",[]),
['#CDVPort',X2,Y2] = proplists:get_value(port,Dict),
ChannelStr2 = integer_to_list(X2),
Port = "#Port<"++ChannelStr2++"."++integer_to_list(Y2)++">",
{ok,_PortDetails=#port{},[]} = crashdump_viewer:port(Port),
+ io:format(" port details ok",[]),
['#CDVPort',X3,Y3] = proplists:get_value(ext_port,Dict),
ChannelStr3 = integer_to_list(X3),
ExtPort = "#Port<"++ChannelStr3++"."++integer_to_list(Y3)++">",
{error,{other_node,ChannelStr3}} = crashdump_viewer:port(ExtPort),
+ io:format(" port details external ok",[]),
{ok,[_Ets=#ets_table{}],[]} = crashdump_viewer:ets_tables(Pid),
- {ok,[_Timer=#timer{}],[]} = crashdump_viewer:timers(Pid),
+ io:format(" ets tables ok",[]),
+
+ {ok,[#timer{pid=Pid0,name=undefined},
+ #timer{pid=Pid0,name="aaaaaaaa"}],[]} =
+ crashdump_viewer:timers(Pid),
+ {ok,AllTimers,_TimersTW} = crashdump_viewer:timers(all),
+ #timer{name="noexistproc"} =
+ lists:keyfind(undefined,#timer.pid,AllTimers),
+ io:format(" timers ok:",[]),
{ok,Mod1=#loaded_mod{},[]} =
crashdump_viewer:loaded_mod_details(atom_to_list(?helper_mod)),
+ io:format(" modules ok",[]),
#loaded_mod{current_size=CS, old_size=OS,
old_attrib=A,old_comp_info=C}=Mod1,
true = is_integer(CS),
@@ -347,6 +405,7 @@ special(File,Procs) ->
true = (C=/=undefined),
{ok,Mod2=#loaded_mod{},[]} =
crashdump_viewer:loaded_mod_details("application"),
+ io:format(" module details ok",[]),
#loaded_mod{old_size="No old code exists",
old_attrib=undefined,
old_comp_info=undefined}=Mod2,
@@ -428,8 +487,10 @@ do_create_dumps(DataDir,Rel) ->
end.
-%% Create a dump which has two visible nodes, one hidden and one
+%% Create a dump which has three visible nodes, one hidden and one
%% not connected node, and with monitors and links between nodes.
+%% One of the visible nodes is stopped and started again in order to
+%% get multiple creations.
full_dist_dump(DataDir,Rel) ->
Opt = rel_opt(Rel),
Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"",
@@ -450,6 +511,15 @@ full_dist_dump(DataDir,Rel) ->
get_response(P4),
get_response(P1),
+ %% start, stop and start a node in order to get multiple 'creations'
+ {ok,N5} = ?t:start_node(n5,peer,Opt ++ PzOpt),
+ P51 = rpc:call(N5,?helper_mod,remote_proc,[P1,Creator]),
+ get_response(P51),
+ ?t:stop_node(N5),
+ {ok,N5} = ?t:start_node(n5,peer,Opt ++ PzOpt),
+ P52 = rpc:call(N5,?helper_mod,remote_proc,[P1,Creator]),
+ get_response(P52),
+
{aaaaaaaa,N1} ! {hello,from,other,node}, % distribution message
?t:stop_node(N3),
@@ -458,6 +528,7 @@ full_dist_dump(DataDir,Rel) ->
?t:stop_node(N2),
?t:stop_node(N4),
+ ?t:stop_node(N5),
CD.
get_response(P) ->
diff --git a/lib/observer/test/observer_SUITE.erl b/lib/observer/test/observer_SUITE.erl
index c076c5e81e..5cf719acb1 100644
--- a/lib/observer/test/observer_SUITE.erl
+++ b/lib/observer/test/observer_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,34 +19,35 @@
-module(observer_SUITE).
-include_lib("test_server/include/test_server.hrl").
+-include_lib("wx/include/wx.hrl").
+-include_lib("observer/src/observer_tv.hrl").
%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
--export([init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0]).
+-export([init_per_testcase/2, end_per_testcase/2,
+ init_per_group/2, end_per_group/2,
+ init_per_suite/1, end_per_suite/1
+ ]).
%% Test cases
--export([app_file/1, appup_file/1]).
+-export([app_file/1, appup_file/1,
+ basic/1, process_win/1, table_win/1
+ ]).
%% Default timetrap timeout (set in init_per_testcase)
-define(default_timeout, ?t:minutes(1)).
-init_per_testcase(_Case, Config) ->
- Dog = ?t:timetrap(?default_timeout),
- [{watchdog, Dog} | Config].
-
-end_per_testcase(_Case, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
- [app_file, appup_file].
+all() ->
+ [app_file, appup_file, {group, gui}].
-groups() ->
- [].
+groups() ->
+ [{gui, [],
+ [basic
+ , process_win, table_win
+ ]
+ }].
init_per_suite(Config) ->
Config.
@@ -54,12 +55,40 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.
-init_per_group(_GroupName, Config) ->
- Config.
+init_per_testcase(_Case, Config) ->
+ Dog = ?t:timetrap(?default_timeout),
+ [{watchdog, Dog} | Config].
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+init_per_group(gui, Config) ->
+ try
+ case os:type() of
+ {unix,darwin} ->
+ exit("Can not test on MacOSX");
+ {unix, _} ->
+ io:format("DISPLAY ~s~n", [os:getenv("DISPLAY")]),
+ case ct:get_config(xserver, none) of
+ none -> ignore;
+ Server -> os:putenv("DISPLAY", Server)
+ end;
+ _ -> ignore
+ end,
+ wx:new(),
+ wx:destroy(),
+ Config
+ catch
+ _:undef ->
+ {skipped, "No wx compiled for this platform"};
+ _:Reason ->
+ SkipReason = io_lib:format("Start wx failed: ~p", [Reason]),
+ {skipped, lists:flatten(SkipReason)}
+ end.
+end_per_group(_, _) ->
+ ok.
app_file(suite) ->
[];
@@ -72,3 +101,178 @@ app_file(Config) when is_list(Config) ->
%% Testing .appup file
appup_file(Config) when is_list(Config) ->
ok = ?t:appup_test(observer).
+
+-define(DBG(Foo), io:format("~p: ~p~n",[?LINE, catch Foo])).
+
+basic(suite) -> [];
+basic(doc) -> [""];
+basic(Config) when is_list(Config) ->
+ timer:send_after(100, "foobar"), %% Otherwise the timer sever gets added to procs
+ ProcsBefore = processes(),
+ NumProcsBefore = length(ProcsBefore),
+
+ ok = observer:start(),
+ Notebook = setup_whitebox_testing(),
+
+ io:format("Notebook ~p~n",[Notebook]),
+ Count = wxNotebook:getPageCount(Notebook),
+ true = Count >= 6,
+ 0 = wxNotebook:getSelection(Notebook),
+ timer:sleep(500),
+ Check = fun(N, TestMore) ->
+ TestMore andalso
+ test_page(wxNotebook:getPageText(Notebook, N),
+ wxNotebook:getCurrentPage(Notebook)),
+ timer:sleep(200),
+ ok = wxNotebook:advanceSelection(Notebook)
+ end,
+ %% Just verify that we can toogle trough all pages
+ [_|_] = [Check(N, false) || N <- lists:seq(1, Count)],
+ %% Cause it to resize
+ Frame = get_top_level_parent(Notebook),
+ {W,H} = wxWindow:getSize(Frame),
+ wxWindow:setSize(Frame, W+10, H+10),
+ [_|_] = [Check(N, true) || N <- lists:seq(0, Count-1)],
+
+ ok = observer:stop(),
+ timer:sleep(2000), %% stop is async
+ ProcsAfter = processes(),
+ NumProcsAfter = length(ProcsAfter),
+ if NumProcsAfter=/=NumProcsBefore ->
+ ct:log("Before but not after:~n~p~n",
+ [[{P,process_info(P)} || P <- ProcsBefore -- ProcsAfter]]),
+ ct:log("After but not before:~n~p~n",
+ [[{P,process_info(P)} || P <- ProcsAfter -- ProcsBefore]]),
+ ct:fail("leaking processes");
+ true ->
+ ok
+ end,
+ ok.
+
+test_page("Load Charts" ++ _, _Window) ->
+ %% Just let it display some info and hopefully it doesn't crash
+ timer:sleep(2000),
+ ok;
+test_page("Applications" ++ _, _Window) ->
+ ok = application:start(mnesia),
+ timer:sleep(1000), %% Give it time to refresh
+ Active = get_active(),
+ FakeEv = #wx{event=#wxCommand{type=command_listbox_selected, cmdString="mnesia"}},
+ Active ! FakeEv,
+ timer:sleep(1000), %% Give it time to refresh
+ ok = application:stop(mnesia),
+ timer:sleep(1000), %% Give it time to refresh
+ ok;
+
+test_page("Processes" ++ _, _Window) ->
+ timer:sleep(500), %% Give it time to refresh
+ Active = get_active(),
+ ChangeSort = fun(N) ->
+ FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
+ Active ! FakeEv,
+ timer:sleep(200)
+ end,
+ [ChangeSort(N) || N <- lists:seq(1,5) ++ [0]],
+ Focus = #wx{event=#wxList{type=command_list_item_focused, itemIndex=2}},
+ Active ! Focus,
+ Activate = #wx{event=#wxList{type=command_list_item_activated}},
+ Active ! Activate,
+ timer:sleep(1000), %% Give it time to refresh
+ ok;
+
+test_page(_Title = "Table" ++ _, _Window) ->
+ Tables = [ets:new(list_to_atom("Test-" ++ [C]), [public]) || C <- lists:seq($A, $Z)],
+ Table = lists:nth(3, Tables),
+ ets:insert(Table, [{N,100-N} || N <- lists:seq(1,100)]),
+
+ Active = get_active(),
+ Active ! refresh_interval,
+ ChangeSort = fun(N) ->
+ FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
+ Active ! FakeEv,
+ timer:sleep(200)
+ end,
+ [ChangeSort(N) || N <- lists:seq(1,5) ++ [0]],
+ timer:sleep(1000),
+ Focus = #wx{event=#wxList{type=command_list_item_selected, itemIndex=2}},
+ Active ! Focus,
+ Activate = #wx{event=#wxList{type=command_list_item_activated, itemIndex=2}},
+ Active ! Activate,
+
+ Info = 407, %% whitebox...
+ Active ! #wx{id=Info},
+ timer:sleep(1000),
+ ok;
+
+test_page(Title, Window) ->
+ io:format("Page ~p: ~p~n", [Title, Window]),
+ %% Just let it display some info and hopefully it doesn't crash
+ timer:sleep(1000),
+ ok.
+
+
+process_win(suite) -> [];
+process_win(doc) -> [""];
+process_win(Config) when is_list(Config) ->
+ ok = observer:start(),
+ ObserverNB = setup_whitebox_testing(),
+ Parent = get_top_level_parent(ObserverNB),
+ Frame = observer_procinfo:start(self(), Parent, self()),
+ PIPid = wx_object:get_pid(Frame),
+ PIPid ! {get_debug_info, self()},
+ Notebook = receive {procinfo_debug, NB} -> NB end,
+ Count = wxNotebook:getPageCount(Notebook),
+ Check = fun(_N) ->
+ ok = wxNotebook:advanceSelection(Notebook),
+ timer:sleep(400)
+ end,
+ [_|_] = [Check(N) || N <- lists:seq(1, Count)],
+ PIPid ! #wx{event=#wxClose{type=close_window}},
+ observer:stop(),
+ ok.
+
+table_win(suite) -> [];
+table_win(doc) -> [""];
+table_win(Config) when is_list(Config) ->
+ Tables = [ets:new(list_to_atom("Test-" ++ [C]), [public]) || C <- lists:seq($A, $Z)],
+ Table = lists:nth(3, Tables),
+ ets:insert(Table, [{N,100-N} || N <- lists:seq(1,100)]),
+ ok = observer:start(),
+ Notebook = setup_whitebox_testing(),
+ Parent = get_top_level_parent(Notebook),
+ TObj = observer_tv_table:start_link(Parent, [{node,node()}, {type,ets}, {table,#tab{name=foo, id=Table}}]),
+ %% Modal can not test edit..
+ %% TPid = wx_object:get_pid(TObj),
+ %% TPid ! #wx{event=#wxList{type=command_list_item_activated, itemIndex=12}},
+ timer:sleep(3000),
+ wx_object:get_pid(TObj) ! #wx{event=#wxClose{type=close_window}},
+ observer:stop(),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_top_level_parent(Window) ->
+ Parent = wxWindow:getParent(Window),
+ case wx:is_null(Parent) of
+ true -> Window;
+ false -> get_top_level_parent(Parent)
+ end.
+
+setup_whitebox_testing() ->
+ %% So that if we die observer exists
+ link(whereis(observer)),
+ {Env, Notebook, _Active} = get_observer_debug(),
+ wx:set_env(Env),
+ Notebook.
+
+get_active() ->
+ {_, _, Active} = get_observer_debug(),
+ Active.
+
+get_observer_debug() ->
+ observer ! {get_debug_info, self()},
+ receive
+ {observer_debug, Env, Notebook, Active} ->
+ {Env, Notebook, Active}
+ end.
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index f48809a839..a6300eeb18 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 1.3.1.2
+OBSERVER_VSN = 2.0
diff --git a/lib/odbc/aclocal.m4 b/lib/odbc/aclocal.m4
index c51c26794a..ed492d55ff 100644
--- a/lib/odbc/aclocal.m4
+++ b/lib/odbc/aclocal.m4
@@ -83,8 +83,10 @@ AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross
AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_LM_CONF, [OSE load module default configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE OSE confd source file])
+AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file])
AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file])
])
@@ -1116,7 +1118,7 @@ case "$THR_LIB_NAME" in
[Define if you have the "ose_spi/ose_spi.h" header file.]))
;;
esac
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
case $host_os in
openbsd*)
# The default stack size is insufficient for our needs
@@ -1220,7 +1222,7 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for functions
dnl
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
AC_CHECK_FUNC(pthread_spin_lock, \
[ethr_have_native_spinlock=yes \
AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml
index b254ca3bc9..0ba2b1ff3f 100644
--- a/lib/odbc/doc/src/notes.xml
+++ b/lib/odbc/doc/src/notes.xml
@@ -31,7 +31,57 @@
<p>This document describes the changes made to the odbc application.
</p>
- <section><title>ODBC 2.10.19</title>
+ <section><title>ODBC 2.10.20</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Removed warnings at compile time by adding missing
+ include file (Thanks to Anthony Ramine)</p>
+ <p>
+ Own Id: OTP-11569</p>
+ </item>
+ <item>
+ <p>
+ Apple has removed iODBC in OS X 10.9 Mavericks, but
+ forgot to remove all binaries, adopt configure so that
+ will be possible to build odbc with own installation.</p>
+ <p>
+ Own Id: OTP-11630</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.10.19</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/odbc/src/odbc.app.src b/lib/odbc/src/odbc.app.src
index 5229b28c08..b2c5775de2 100644
--- a/lib/odbc/src/odbc.app.src
+++ b/lib/odbc/src/odbc.app.src
@@ -11,5 +11,6 @@
]},
{applications, [kernel, stdlib]},
{env,[]},
- {mod, {odbc_app, []}}]}.
+ {mod, {odbc_app, []}},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index d9e2ab26a9..1af4751248 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1 +1 @@
-ODBC_VSN = 2.10.19
+ODBC_VSN = 2.10.20
diff --git a/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml
index c4d13a5a2f..93dc403c47 100644
--- a/lib/orber/doc/src/notes.xml
+++ b/lib/orber/doc/src/notes.xml
@@ -33,7 +33,26 @@
</header>
- <section><title>Orber 3.6.26.1</title>
+ <section><title>Orber 3.6.27</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Some local implementations of removing the last element
+ from a list are replaced by <c>lists:droplast/1</c>. Note
+ that this requires at least <c>stdlib-2.0</c>, which is
+ the stdlib version delivered in OTP 17.0. (Thanks to Hans
+ Svensson)</p>
+ <p>
+ Own Id: OTP-11678</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Orber 3.6.26.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/orber/src/orber.app.src b/lib/orber/src/orber.app.src
index 88df4162b6..30bd90347d 100644
--- a/lib/orber/src/orber.app.src
+++ b/lib/orber/src/orber.app.src
@@ -103,7 +103,9 @@
orber_iiop_pm, orber_env]},
{applications, [stdlib, kernel, mnesia]},
{env, []},
- {mod, {orber, []}}
+ {mod, {orber, []}},
+ {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","mnesia-4.12","kernel-3.0",
+ "inets-5.10","erts-6.0"]}
]}.
diff --git a/lib/orber/vsn.mk b/lib/orber/vsn.mk
index 7bbebc65dc..3ea64b1ff6 100644
--- a/lib/orber/vsn.mk
+++ b/lib/orber/vsn.mk
@@ -1,2 +1,2 @@
-ORBER_VSN = 3.6.26.1
+ORBER_VSN = 3.6.27
diff --git a/lib/os_mon/c_src/memsup.c b/lib/os_mon/c_src/memsup.c
index b5114d10ed..409db84aa7 100644
--- a/lib/os_mon/c_src/memsup.c
+++ b/lib/os_mon/c_src/memsup.c
@@ -324,7 +324,7 @@ get_mem_procfs(memory_ext *me){
/* arch specific functions */
-#if defined(__linux__) /* ifdef SYSINFO */
+#if defined(__linux__) && !defined(__ANDROID__)/* ifdef SYSINFO */
/* sysinfo does not include cached memory which is a problem. */
static int
get_extended_mem_sysinfo(memory_ext *me) {
@@ -395,8 +395,12 @@ get_extended_mem_sgi(memory_ext *me) {
static void
get_extended_mem(memory_ext *me) {
+/* android */
+#if defined(__ANDROID__)
+ if (get_mem_procfs(me)) return;
+
/* linux */
-#if defined(__linux__)
+#elif defined(__linux__)
if (get_mem_procfs(me)) return;
if (get_extended_mem_sysinfo(me)) return;
diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml
index 3a7d7793d4..5ac04d4f42 100644
--- a/lib/os_mon/doc/src/notes.xml
+++ b/lib/os_mon/doc/src/notes.xml
@@ -30,6 +30,48 @@
</header>
<p>This document describes the changes made to the OS_Mon application.</p>
+<section><title>Os_Mon 2.2.15</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Calls to erlang:open_port/2 with 'spawn' are updated to
+ handle space in the command path.</p>
+ <p>
+ Own Id: OTP-10842</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Os_Mon 2.2.14</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/os_mon/src/os_mon.app.src b/lib/os_mon/src/os_mon.app.src
index 15bbd2663c..cc08cebe3d 100644
--- a/lib/os_mon/src/os_mon.app.src
+++ b/lib/os_mon/src/os_mon.app.src
@@ -29,4 +29,7 @@
{start_disksup, true},
{start_memsup, true},
{start_os_sup, false}]},
- {mod, {os_mon, []}}]}.
+ {mod, {os_mon, []}},
+ {runtime_dependencies, ["stdlib-2.0","snmp-4.25.1","sasl-2.4",
+ "otp_mibs-1.0.9","mnesia-4.12","kernel-3.0",
+ "erts-6.0"]}]}.
diff --git a/lib/os_mon/test/cpu_sup_SUITE.erl b/lib/os_mon/test/cpu_sup_SUITE.erl
index e0382cb0c7..9f58e043db 100644
--- a/lib/os_mon/test/cpu_sup_SUITE.erl
+++ b/lib/os_mon/test/cpu_sup_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -257,8 +257,8 @@ port(Config) when is_list(Config) ->
terminate(suite) ->
[];
terminate(Config) when is_list(Config) ->
- ?line ok = application:set_env(os_mon, start_cpu_sup, false),
- ?line ok = supervisor:terminate_child(os_mon_sup, cpu_sup),
+ ok = application:set_env(os_mon, start_cpu_sup, false),
+ _ = supervisor:terminate_child(os_mon_sup, cpu_sup),
ok.
unavailable(suite) ->
diff --git a/lib/os_mon/test/disksup_SUITE.erl b/lib/os_mon/test/disksup_SUITE.erl
index 9c65d8b692..94661cfa77 100644
--- a/lib/os_mon/test/disksup_SUITE.erl
+++ b/lib/os_mon/test/disksup_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -350,15 +350,16 @@ otp_5910(doc) ->
otp_5910(Config) when is_list(Config) ->
%% Make sure disksup sets at least one alarm
- Data = disksup:get_disk_data(),
+ Data = lists:sort(disksup:get_disk_data()),
Threshold0 = disksup:get_almost_full_threshold(),
Threshold = case over_threshold(Data, Threshold0) of
- 0 ->
- [{_Id,_Kbyte,Cap}|_] = Data,
- ok = disksup:set_almost_full_threshold((Cap-1)/100),
- Cap-1;
- _N -> Threshold0
- end,
+ 0 ->
+ [{_Id,_Kbyte,Cap}|_] = Data,
+ io:format("Data ~p Threshold ~p ~n",[Data, Cap-1]),
+ ok = disksup:set_almost_full_threshold((Cap-1)/100),
+ Cap-1;
+ _N -> Threshold0
+ end,
ok = application:set_env(os_mon, disk_almost_full_threshold, Threshold/100),
disksup ! timeout, % force a disk check
Data2 = disksup:get_disk_data(),
diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
index e9e90729f2..74397c2bc6 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.2.14
+OS_MON_VSN = 2.2.15
diff --git a/lib/ose/doc/src/ose_intro.xml b/lib/ose/doc/src/ose_intro.xml
index 0dd3ec409e..b5e3ef8b33 100644
--- a/lib/ose/doc/src/ose_intro.xml
+++ b/lib/ose/doc/src/ose_intro.xml
@@ -78,7 +78,7 @@ rtose@acp3400> pm_start 0x110059</code>
seperate machine the paths have to be updated. In the example above
<c>/usr/local/lib/erlang</c> was replaced by <c>/mst/erlang/</c>. The
goal is to in future releases not have to do the special argument handling
- but for now (17.0-rc2) you have to do it.
+ but for now (OTP 17.0) you have to do it.
</p>
<note>
Because of a limitation in the way the OSE handles stdio when starting
@@ -146,7 +146,7 @@ os:type().
It is possible to set the priorities you want for the OSE processes that
thr emulator creates in the lmconf. An example of how to do it can be
found in the default lmconf file in
- $ERL_TOP/erts/emulator/sys/ose/default.lmconf.
+ $ERL_TOP/erts/emulator/sys/ose/beam.lmconf.
</p>
</section>
diff --git a/lib/ose/src/ose.app.src b/lib/ose/src/ose.app.src
index c39d3f2d05..60699c369b 100644
--- a/lib/ose/src/ose.app.src
+++ b/lib/ose/src/ose.app.src
@@ -23,4 +23,5 @@
{modules, [ose]},
{registered,[]},
{applications, [stdlib,kernel]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","erts-6.0"]}]}.
diff --git a/lib/otp_mibs/doc/src/notes.xml b/lib/otp_mibs/doc/src/notes.xml
index 0c13fb2349..391c82b1c5 100644
--- a/lib/otp_mibs/doc/src/notes.xml
+++ b/lib/otp_mibs/doc/src/notes.xml
@@ -31,6 +31,55 @@
<p>This document describes the changes made to the OTP_Mibs
application.</p>
+<section><title>Otp_Mibs 1.0.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add type based integer value truncation/reset.</p>
+ <p>
+ This fixes errors when querying e.g. the
+ erlNodeReductions, erlNodeInBytes and erlNodeOutBytes
+ objects in long-running Erlang/OTP systems.</p>
+ <p>
+ Update types of applicable MIB objects to 64bit based
+ types.</p>
+ <p>
+ Potential incompatibility: Type change of Counter32 to
+ Counter64 in OTP-MIB.mib</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11203</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Otp_Mibs 1.0.8</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/otp_mibs/src/otp_mibs.app.src b/lib/otp_mibs/src/otp_mibs.app.src
index b177af0709..ebc656b0b2 100644
--- a/lib/otp_mibs/src/otp_mibs.app.src
+++ b/lib/otp_mibs/src/otp_mibs.app.src
@@ -23,5 +23,7 @@
{modules, [otp_mib]},
{registered, []},
{applications, [kernel, stdlib, snmp]},
- {env,[]}]}.
+ {env,[]},
+ {runtime_dependencies, ["stdlib-2.0","snmp-4.25.1","mnesia-4.12",
+ "kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/otp_mibs/vsn.mk b/lib/otp_mibs/vsn.mk
index 96d3088224..98db21c132 100644
--- a/lib/otp_mibs/vsn.mk
+++ b/lib/otp_mibs/vsn.mk
@@ -1,4 +1,4 @@
-OTP_MIBS_VSN = 1.0.8
+OTP_MIBS_VSN = 1.0.9
# Note: The branch 'otp_mibs' is defunct as of otp_mibs-1.0.4 and
# should NOT be used again.
diff --git a/lib/parsetools/doc/src/notes.xml b/lib/parsetools/doc/src/notes.xml
index 929daedf74..a8368740da 100644
--- a/lib/parsetools/doc/src/notes.xml
+++ b/lib/parsetools/doc/src/notes.xml
@@ -30,6 +30,42 @@
</header>
<p>This document describes the changes made to the Parsetools application.</p>
+<section><title>Parsetools 2.0.11</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ <item>
+ <p>
+ A Yecc example has been updated in the documentation
+ (Thanks to Pierre Fenoll.)</p>
+ <p>
+ Own Id: OTP-11749</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Parsetools 2.0.10</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/parsetools/src/parsetools.app.src b/lib/parsetools/src/parsetools.app.src
index af62fc4f6b..9eeb8fcc05 100644
--- a/lib/parsetools/src/parsetools.app.src
+++ b/lib/parsetools/src/parsetools.app.src
@@ -11,7 +11,8 @@
{applications, [kernel,stdlib]},
{env, [{file_util_search_methods,[{"", ""}, {"ebin", "esrc"}, {"ebin", "src"}]}
]
- }
+ },
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}
]
}.
diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk
index d62962c54a..8fd7422c1c 100644
--- a/lib/parsetools/vsn.mk
+++ b/lib/parsetools/vsn.mk
@@ -1 +1 @@
-PARSETOOLS_VSN = 2.0.10
+PARSETOOLS_VSN = 2.0.11
diff --git a/lib/percept/doc/src/notes.xml b/lib/percept/doc/src/notes.xml
index 3cead186c4..bae999ed1a 100644
--- a/lib/percept/doc/src/notes.xml
+++ b/lib/percept/doc/src/notes.xml
@@ -32,6 +32,35 @@
</header>
<p>This document describes the changes made to the Percept application.</p>
+<section><title>Percept 0.8.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Percept 0.8.8.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/percept/src/percept.app.src b/lib/percept/src/percept.app.src
index cf4a9fc438..f8991ee577 100644
--- a/lib/percept/src/percept.app.src
+++ b/lib/percept/src/percept.app.src
@@ -35,7 +35,9 @@
]},
{registered, [percept_db,percept_port]},
{applications, [kernel,stdlib]},
- {env,[]}
+ {env,[]},
+ {runtime_dependencies, ["stdlib-2.0","runtime_tools-1.8.14","kernel-3.0",
+ "inets-5.10","erts-6.0"]}
]}.
diff --git a/lib/percept/vsn.mk b/lib/percept/vsn.mk
index 99729c11e2..935a9d1336 100644
--- a/lib/percept/vsn.mk
+++ b/lib/percept/vsn.mk
@@ -1 +1 @@
-PERCEPT_VSN = 0.8.8.2
+PERCEPT_VSN = 0.8.9
diff --git a/lib/public_key/doc/src/cert_records.xml b/lib/public_key/doc/src/cert_records.xml
index 79e5cb219d..397c13b463 100644
--- a/lib/public_key/doc/src/cert_records.xml
+++ b/lib/public_key/doc/src/cert_records.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -39,7 +39,7 @@
The intent is to describe the data types and not to specify the meaning of each
component for this we refer you to <url
href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280</url> and
- <url href="http://www.rsa.com/rsalabs/node.asp?id=2124">PKCS-10</url>.
+ <url href="http://www.ietf.org/rfc/rfc5967.txt">PKCS-10</url>.
</p>
<p>Use the following include directive to get access to the
diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml
index 1dce718ea3..592d3c797d 100644
--- a/lib/public_key/doc/src/notes.xml
+++ b/lib/public_key/doc/src/notes.xml
@@ -34,6 +34,70 @@
<file>notes.xml</file>
</header>
+<section><title>Public_Key 0.22</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix incorrect dialyzer spec and types, also enhance
+ documentation. </p>
+ <p>
+ Thanks to Ayaz Tuncer.</p>
+ <p>
+ Own Id: OTP-11627</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Moved elliptic curve definition from the crypto
+ NIF/OpenSSL into Erlang code, adds the RFC-5639 brainpool
+ curves and makes TLS use them (RFC-7027).</p>
+ <p>
+ Thanks to Andreas Schultz</p>
+ <p>
+ Own Id: OTP-11578</p>
+ </item>
+ <item>
+ <p>
+ Handle v1 CRLs, with no extensions and fixes issues with
+ IDP (Issuing Distribution Point) comparison during CRL
+ validation. </p>
+ <p>
+ Thanks to Andrew Thompson</p>
+ <p>
+ Own Id: OTP-11761</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 0.21</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/public_key/doc/src/part.xml b/lib/public_key/doc/src/part.xml
index 51e628aa90..73146c8e2a 100644
--- a/lib/public_key/doc/src/part.xml
+++ b/lib/public_key/doc/src/part.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -34,7 +34,7 @@
<p> This application provides an API to public key infrastructure
from <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC
5280</url> (X.509 certificates) and public key formats defined by
- the <url href="http://www.rsa.com/rsalabs/node.asp?id=2124">
+ the <url href="http://en.wikipedia.org/wiki/PKCS">
PKCS-standard</url></p>
</description>
<xi:include href="introduction.xml"/>
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index fc3479cb64..8e93f562d4 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>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -48,12 +48,12 @@
<item>Supports <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280 </url> -
Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile </item>
- <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2125"> PKCS-1 </url> - RSA Cryptography Standard </item>
+ <item>Supports <url href="http://www.ietf.org/rfc/rfc3447.txt"> PKCS-1 </url> - RSA Cryptography Standard </item>
<item>Supports <url href="http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf"> DSS</url>- Digital Signature Standard (DSA - Digital Signature Algorithm)</item>
- <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2126"> PKCS-3 </url> - Diffie-Hellman Key Agreement Standard </item>
- <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2127"> PKCS-5</url> - Password-Based Cryptography Standard </item>
- <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2130"> PKCS-8</url> - Private-Key Information Syntax Standard</item>
- <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2132"> PKCS-10</url> - Certification Request Syntax Standard</item>
+ <item>Supports <url href="http://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-3-diffie-hellman-key-agreement-standar.htm"> PKCS-3 </url> - Diffie-Hellman Key Agreement Standard </item>
+ <item>Supports <url href="http://www.ietf.org/rfc/rfc2898.txt"> PKCS-5</url> - Password-Based Cryptography Standard </item>
+ <item>Supports <url href="http://www.ietf.org/rfc/rfc5208.txt"> PKCS-8</url> - Private-Key Information Syntax Standard</item>
+ <item>Supports <url href="http://www.ietf.org/rfc/rfc5967.txt"> PKCS-10</url> - Certification Request Syntax Standard</item>
</list>
</section>
@@ -461,11 +461,14 @@
<p>The fun should be defined as:</p>
<code>
-fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
- {extension, #'Extension'{}},
+fun(OtpCert :: #'OTPCertificate'{},
+ Event :: {bad_cert, Reason :: atom()} |
+ {extension, #'Extension'{}},
InitialUserState :: term()) ->
- {valid, UserState :: term()} | {valid_peer, UserState :: term()} |
- {fail, Reason :: term()} | {unknown, UserState :: term()}.
+ {valid, UserState :: term()} |
+ {valid_peer, UserState :: term()} |
+ {fail, Reason :: term()} |
+ {unknown, UserState :: term()}.
</code>
<p>If the verify callback fun returns {fail, Reason}, the
@@ -511,7 +514,8 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
<item>
<p>The fun has the following type spec:</p>
- <code> fun(#'DistributionPoint'{}, #'CertificateList'{}) -> #'CertificateList'{}</code>
+ <code> fun(#'DistributionPoint'{}, #'CertificateList'{}) ->
+ #'CertificateList'{}</code>
<p>The fun should use the information in the distribution point to acesses
the lates possible version of the CRL. If this fun is not specified
@@ -519,6 +523,21 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
</p>
<code> fun(_DP, CRL) -> CRL end</code>
</item>
+
+ <tag>{issuer_fun, fun()}</tag>
+ <item>
+ <p>The fun has the following type spec:</p>
+
+ <code>
+fun(#'DistributionPoint'{}, #'CertificateList'{},
+ {rdnSequence,[#'AttributeTypeAndValue'{}]}, term()) ->
+ {ok, #'OTPCertificate'{}, [der_encoded]}</code>
+
+ <p>The fun should return the root certificate and certificate chain
+ that has signed the CRL.
+ </p>
+ <code> fun(DP, CRL, Issuer, UserState) -> {ok, RootCert, CertChain}</code>
+ </item>
</taglist>
</desc>
</func>
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index dc8d68c78f..ae517ca642 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -319,6 +319,8 @@ verify_fun(Otpcert, Result, UserState0, VerifyFun) ->
%%
%% Description: Extracts a specific extension from a list of extensions.
%%--------------------------------------------------------------------
+select_extension(_, asn1_NOVALUE) ->
+ undefined;
select_extension(_, []) ->
undefined;
select_extension(Id, [#'Extension'{extnID = Id} = Extension | _]) ->
@@ -342,8 +344,11 @@ match_name(uniformResourceIdentifier, URI, [PermittedName | Rest]) ->
incomplete ->
false;
{_, _, Host, _, _} ->
- match_name(fun is_valid_host_or_domain/2, Host,
- PermittedName, Rest)
+ PN = case split_uri(PermittedName) of
+ {_, _, PNhost, _, _} -> PNhost;
+ _X -> PermittedName
+ end,
+ match_name(fun is_valid_host_or_domain/2, Host, PN, Rest)
end;
match_name(emailAddress, Name, [PermittedName | Rest]) ->
@@ -511,10 +516,10 @@ is_dir_name2(Value, Value) -> true;
is_dir_name2({printableString, Value1}, {printableString, Value2}) ->
string:to_lower(strip_spaces(Value1)) =:=
string:to_lower(strip_spaces(Value2));
-is_dir_name2({utf8String, Value1}, String) -> %% BUGBUG FIX UTF8 conv
- is_dir_name2({printableString, binary_to_list(Value1)}, String);
-is_dir_name2(String, {utf8String, Value1}) -> %% BUGBUG FIX UTF8 conv
- is_dir_name2(String, {printableString, binary_to_list(Value1)});
+is_dir_name2({utf8String, Value1}, String) ->
+ is_dir_name2({printableString, unicode:characters_to_list(Value1)}, String);
+is_dir_name2(String, {utf8String, Value1}) ->
+ is_dir_name2(String, {printableString, unicode:characters_to_list(Value1)});
is_dir_name2(_, _) ->
false.
diff --git a/lib/public_key/src/pubkey_crl.erl b/lib/public_key/src/pubkey_crl.erl
index eaba5bfa1b..f0df4bc3f2 100644
--- a/lib/public_key/src/pubkey_crl.erl
+++ b/lib/public_key/src/pubkey_crl.erl
@@ -39,7 +39,13 @@ validate(OtpCert, OtherDPCRLs, DP, {DerCRL, CRL}, {DerDeltaCRL, DeltaCRL},
CertIssuer = TBSCert#'OTPTBSCertificate'.issuer,
TBSCRL = CRL#'CertificateList'.tbsCertList,
CRLIssuer = TBSCRL#'TBSCertList'.issuer,
- AltNames = subject_alt_names(TBSCert#'OTPTBSCertificate'.extensions),
+ AltNames = case pubkey_cert:select_extension(?'id-ce-subjectAltName',
+ TBSCert#'OTPTBSCertificate'.extensions) of
+ undefined ->
+ [];
+ Ext ->
+ Ext#'Extension'.extnValue
+ end,
revoked_status(DP, IDP, {directoryName, CRLIssuer},
[ {directoryName, CertIssuer} | AltNames], SerialNumber, Revoked,
DeltaRevoked, RevokedState1);
@@ -397,16 +403,18 @@ verify_dp_name(IDPNames, DPorIssuerNames) ->
match_one([], _) ->
false;
match_one([{Type, Name} | Names], CandidateNames) ->
- Candidates = [NameName || {NameType, NameName} <- CandidateNames, NameType == Type],
+ Candidates = [NameName || {NameType, NameName} <- CandidateNames,
+ NameType == Type],
case Candidates of
[] ->
false;
- [_|_] -> case pubkey_cert:match_name(Type, Name, Candidates) of
- true ->
- true;
- false ->
- match_one(Names, CandidateNames)
- end
+ [_|_] ->
+ case pubkey_cert:match_name(Type, Name, Candidates) of
+ true ->
+ true;
+ false ->
+ match_one(Names, CandidateNames)
+ end
end.
verify_dp_bools(TBSCert, IDP) ->
@@ -664,6 +672,8 @@ verify_extensions([#'TBSCertList_revokedCertificates_SEQOF'{crlEntryExtensions =
verify_extensions(pubkey_cert:extensions_list(Ext)) and verify_extensions(Rest);
verify_extensions([]) ->
true;
+verify_extensions(asn1_NOVALUE) ->
+ true;
verify_extensions([#'Extension'{critical = true, extnID = Id} | Rest]) ->
case lists:member(Id, [?'id-ce-authorityKeyIdentifier',
?'id-ce-issuerAltName',
@@ -689,13 +699,3 @@ authority_key_identifier(Extensions) ->
Enc = extension_value(?'id-ce-authorityKeyIdentifier',
'AuthorityKeyIdentifier', Extensions),
pubkey_cert_records:transform(Enc, decode).
-
-subject_alt_names(Extensions) ->
- Enc = extension_value(?'id-ce-subjectAltName',
- 'GeneralNames', Extensions),
- case Enc of
- undefined ->
- [];
- _ ->
- pubkey_cert_records:transform(Enc, decode)
- end.
diff --git a/lib/public_key/src/public_key.app.src b/lib/public_key/src/public_key.app.src
index 736a778a4b..88ef07c5a6 100644
--- a/lib/public_key/src/public_key.app.src
+++ b/lib/public_key/src/public_key.app.src
@@ -13,7 +13,9 @@
]},
{applications, [asn1, crypto, kernel, stdlib]},
{registered, []},
- {env, []}
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0","crypto-3.3",
+ "asn1-3.0"]}
]
}.
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index 3473757c5f..f0450918aa 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 0.21
+PUBLIC_KEY_VSN = 0.22
diff --git a/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml
index df81418677..969af2d745 100644
--- a/lib/reltool/doc/src/notes.xml
+++ b/lib/reltool/doc/src/notes.xml
@@ -37,7 +37,53 @@
thus constitutes one section in this document. The title of each
section is the version number of Reltool.</p>
- <section><title>Reltool 0.6.4.1</title>
+ <section><title>Reltool 0.6.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When adding a regexp to a filter in reltool using
+ {add,Regexp}, and the existing regexp was undefined,
+ reltool would crash since it got an improper list. This
+ has been corrected. (Thanks to HÃ¥kan Mattsson)</p>
+ <p>
+ Own Id: OTP-11591</p>
+ </item>
+ <item>
+ <p>
+ Adapted reltool test server to common test usage of
+ tc_status. (Note that this code is not used by OTP daily
+ test runs.) (Thanks to HÃ¥kan Mattsson)</p>
+ <p>
+ Own Id: OTP-11592</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Reltool 0.6.4.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/reltool/src/reltool.app.src b/lib/reltool/src/reltool.app.src
index 4188f341f1..65fcf4aae5 100644
--- a/lib/reltool/src/reltool.app.src
+++ b/lib/reltool/src/reltool.app.src
@@ -34,5 +34,7 @@
]},
{registered, []},
{applications, [stdlib, kernel]},
- {env, []}
+ {env, []},
+ {runtime_dependencies, ["wx-1.2","tools-2.6.14","stdlib-2.0","sasl-2.4",
+ "kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index f0d8b38519..56161a152a 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -164,7 +164,8 @@
applications = [] :: [app_name()],
env = [] :: [{atom(), term()}],
mod = undefined :: {mod_name(), [term()]} | undefined,
- start_phases = undefined :: [{atom(), term()}] | undefined
+ start_phases = undefined :: [{atom(), term()}] | undefined,
+ runtime_dependencies = [] :: [string()]
}).
-record(regexp, {source, compiled}).
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 97785ca7f8..98eeed5c27 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -1125,6 +1125,9 @@ parse_app_info(File, [{Key, Val} | KeyVals], AI, Status) ->
start_phases ->
parse_app_info(File, KeyVals, AI#app_info{start_phases = Val},
Status);
+ runtime_dependencies ->
+ parse_app_info(File, KeyVals, AI#app_info{runtime_dependencies = Val},
+ Status);
_ ->
Status2 =
reltool_utils:add_warning("Unexpected item ~p in app file ~tp.",
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index bfe5d39d53..b3b7afd1a9 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1205,14 +1205,9 @@ create_slim(Config) ->
RootDir = code:root_dir(),
Erl = filename:join([RootDir, "bin", "erl"]),
- EscapedQuote =
- case os:type() of
- {win32,_} -> "\\\"";
- _ -> "\""
- end,
Args = ["-boot_var", "RELTOOL_EXT_LIB", TargetLibDir,
"-boot", filename:join(TargetRelVsnDir,RelName),
- "-sasl", "releases_dir", EscapedQuote++TargetRelDir++EscapedQuote],
+ "-sasl", "releases_dir", "\""++TargetRelDir++"\""],
{ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl, Args)),
?msym(RootDir, rpc:call(Node, code, root_dir, [])),
wait_for_app(Node,sasl,50),
diff --git a/lib/reltool/test/reltool_test_lib.erl b/lib/reltool/test/reltool_test_lib.erl
index 530d0a9985..fa12f19aa7 100644
--- a/lib/reltool/test/reltool_test_lib.erl
+++ b/lib/reltool/test/reltool_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,12 +20,13 @@
-compile(export_all).
-include("reltool_test_lib.hrl").
+-define(timeout, 20). % minutes
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_per_suite(Config) when is_list(Config)->
global:register_name(reltool_global_logger, group_leader()),
- incr_timetrap(Config, 10).
+ incr_timetrap(Config, ?timeout).
end_per_suite(Config) when is_list(Config)->
global:unregister_name(reltool_global_logger),
@@ -51,7 +52,7 @@ set_kill_timer(Config) ->
Time =
case lookup_config(tc_timeout, Config) of
[] ->
- timer:minutes(10);
+ timer:minutes(?timeout);
ConfigTime when is_integer(ConfigTime) ->
ConfigTime
end,
diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk
index 16ec570d22..163b77dfa0 100644
--- a/lib/reltool/vsn.mk
+++ b/lib/reltool/vsn.mk
@@ -1 +1 @@
-RELTOOL_VSN = 0.6.4.1
+RELTOOL_VSN = 0.6.5
diff --git a/lib/runtime_tools/doc/specs/.gitignore b/lib/runtime_tools/doc/specs/.gitignore
new file mode 100644
index 0000000000..322eebcb06
--- /dev/null
+++ b/lib/runtime_tools/doc/specs/.gitignore
@@ -0,0 +1 @@
+specs_*.xml
diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile
index 51d93df418..07c63197e9 100644
--- a/lib/runtime_tools/doc/src/Makefile
+++ b/lib/runtime_tools/doc/src/Makefile
@@ -40,7 +40,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml
+XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml system_information.xml
XML_REF6_FILES = runtime_tools_app.xml
XML_PART_FILES = part_notes.xml part_notes_history.xml part.xml
@@ -71,12 +71,20 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+
+TOP_SPECS_FILE = specs.xml
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
XML_FLAGS +=
DVIPS_FLAGS +=
+SPECS_ESRC = ../../src
+
+SPECS_FLAGS = -I../../include -I../../../kernel/src
+
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml
index 32b7d168f5..9b026aee11 100644
--- a/lib/runtime_tools/doc/src/notes.xml
+++ b/lib/runtime_tools/doc/src/notes.xml
@@ -31,6 +31,95 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.8.14</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The documentation for the return value of
+ dbg:{stop,stop_clear} functions are now correct (Thanks
+ to Luca Favatella)</p>
+ <p>
+ Own Id: OTP-11603</p>
+ </item>
+ <item>
+ <p>
+ Fix DTrace build on Illumos. (Thanks to Ryan Zezeski.)</p>
+ <p>
+ Own Id: OTP-11622</p>
+ </item>
+ <item>
+ <p>
+ Do not turn off scheduler_wall_time, as it can interfere
+ with other applications usage.</p>
+ <p>
+ Own Id: OTP-11693 Aux Id: seq12528 </p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Allow install path to have unicode characters.</p>
+ <p>
+ Own Id: OTP-10877</p>
+ </item>
+ <item>
+ <p>
+ The <c>erts_alloc_config</c> tool has been updated to
+ produce configurations that better fit todays SMP support
+ in the VM.</p>
+ <p>
+ Own Id: OTP-11662</p>
+ </item>
+ <item>
+ <p>
+ The <seealso
+ marker="kernel:app"><c>app</c></seealso>-file key
+ <seealso
+ marker="kernel:app#runtime_dependencies"><c>runtime_dependencies</c></seealso>
+ has been introduced.</p>
+ <p>
+ Runtime dependencies have been added to all app-files in
+ OTP. Note that these may not be completely correct during
+ OTP 17, but this is actively being worked on.</p>
+ <p>
+ The function <seealso
+ marker="runtime_tools:system_information#sanity_check/0"><c>system_information:sanity_check/0</c></seealso>
+ will verify all declared runtime dependencies in the
+ system when called.</p>
+ <p>
+ Own Id: OTP-11773</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.8.13</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/runtime_tools/doc/src/ref_man.xml b/lib/runtime_tools/doc/src/ref_man.xml
index 6017f3cdaa..25fa97896b 100644
--- a/lib/runtime_tools/doc/src/ref_man.xml
+++ b/lib/runtime_tools/doc/src/ref_man.xml
@@ -35,5 +35,6 @@
<xi:include href="dbg.xml"/>
<xi:include href="dyntrace.xml"/>
<xi:include href="erts_alloc_config.xml"/>
+ <xi:include href="system_information.xml"/>
</application>
diff --git a/lib/runtime_tools/doc/src/specs.xml b/lib/runtime_tools/doc/src/specs.xml
new file mode 100644
index 0000000000..d4c3c9dfe6
--- /dev/null
+++ b/lib/runtime_tools/doc/src/specs.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<specs xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="../specs/specs_system_information.xml"/>
+</specs>
diff --git a/lib/runtime_tools/doc/src/system_information.xml b/lib/runtime_tools/doc/src/system_information.xml
new file mode 100644
index 0000000000..b586334ae7
--- /dev/null
+++ b/lib/runtime_tools/doc/src/system_information.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2014</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title></title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno>1</docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
+ <file>system_information.xml</file>
+ </header>
+ <module>system_information</module>
+ <modulesummary>System Information</modulesummary>
+ <description>
+ <p></p>
+ </description>
+ <funcs>
+ <func>
+ <name name="sanity_check" arity="0"/>
+ <fsummary>Perform a sanity check</fsummary>
+ <desc>
+ <p>Performs a sanity check on the system. If no issues
+ were found, <c>ok</c> is returned. If issues were
+ found, <c>{failed, <anno>Failures</anno>}</c> is
+ returned. All failures found will be part of the
+ <c><anno>Failures</anno></c> list. Currently defined
+ <c><anno>Failure</anno></c> elements in the
+ <c><anno>Failures</anno></c> list:</p>
+ <taglist>
+ <tag><c><anno>InvalidAppFile</anno></c></tag>
+ <item><p>An application has an invalid <c>.app</c> file. The
+ second element identifies the application which has the
+ invalid <c>.app</c> file.</p></item>
+ <tag><c><anno>InvalidApplicationVersion</anno></c></tag>
+ <item><p>An application has an invalid application version.
+ The second element identifies the application version that
+ is invalid.</p></item>
+ <tag><c><anno>MissingRuntimeDependencies</anno></c></tag>
+ <item><p>An application is missing
+ <seealso marker="kernel:app#runtime_dependencies">runtime
+ dependencies</seealso>. The second element identifies the
+ application (with version) that has missing dependencies.
+ The third element contains the missing dependencies.</p>
+ <p>Note that this check use application versions that
+ are loaded, or will be loaded when used. You might have
+ application versions that satisfies all dependencies
+ installed in the system, but if those are not loaded this
+ check will fail. The system will of course also fail when
+ used like this. This may happen when you have multiple
+ <seealso marker="doc/system_principles:versions">branched
+ versions</seealso> of the same application installed in the
+ system, but you do not use a
+ <seealso marker="doc/system_principles:system_principles#BOOTSCRIPT">boot
+ script</seealso> identifing the correct application version.</p>
+ </item>
+ </taglist>
+ <p>Currently the sanity check is limited to verifying
+ runtime dependencies found in the <c>.app</c> files of
+ all applications. More checks will be introduced in the
+ future. This implies that the return type <em>will</em>
+ change in the future.</p>
+ <note><p>An <c>ok</c> return value only means that
+ <c>sanity_check/0</c> did not find any issues, <em>not</em>
+ that no issues exist.</p></note>
+ </desc>
+ </func>
+ <func>
+ <name name="to_file" arity="1"/>
+ <fsummary>Write miscellaneous system information to file</fsummary>
+ <desc><p>Writes miscellaneous system information to file. This
+ information will typically be requested by the Erlang/OTP team
+ at Ericsson AB when reporting an issue.</p></desc>
+ </func>
+ </funcs>
+ </erlref>
+
diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src
index d46cfe1f32..0a70802c08 100644
--- a/lib/runtime_tools/src/runtime_tools.app.src
+++ b/lib/runtime_tools/src/runtime_tools.app.src
@@ -25,6 +25,8 @@
{registered, [runtime_tools_sup]},
{applications, [kernel, stdlib]},
{env, []},
- {mod, {runtime_tools, []}}]}.
+ {mod, {runtime_tools, []}},
+ {runtime_dependencies, ["stdlib-2.0","mnesia-4.12","kernel-3.0",
+ "erts-6.0"]}]}.
diff --git a/lib/runtime_tools/src/system_information.erl b/lib/runtime_tools/src/system_information.erl
index 603b698d5e..f541d6e449 100644
--- a/lib/runtime_tools/src/system_information.erl
+++ b/lib/runtime_tools/src/system_information.erl
@@ -39,7 +39,8 @@
application/1, application/2,
environment/0, environment/1,
module/1, module/2,
- modules/1
+ modules/1,
+ sanity_check/0
]).
%% gen_server callbacks
@@ -85,9 +86,14 @@ report() -> [
{erts_compile_info, erlang:system_info(compile_info)},
{beam_dynamic_libraries, get_dynamic_libraries()},
{environment_erts, os_getenv_erts_specific()},
- {environment, [split_env(Env) || Env <- os:getenv()]}
+ {environment, [split_env(Env) || Env <- os:getenv()]},
+ {sanity_check, sanity_check()}
].
+-spec to_file(FileName) -> ok | {error, Reason} when
+ FileName :: file:name_all(),
+ Reason :: file:posix() | badarg | terminated | system_limit.
+
to_file(File) ->
file:write_file(File, iolist_to_binary([
io_lib:format("{system_information_version, ~p}.~n", [
@@ -130,6 +136,27 @@ module(M, Opts) when is_atom(M), is_list(Opts) ->
modules(Opt) when is_atom(Opt) ->
gen_server:call(?SERVER, {modules, Opt}).
+
+-spec sanity_check() -> ok | {failed, Failures} when
+ Application :: atom(),
+ ApplicationVersion :: string(),
+ MissingRuntimeDependencies :: {missing_runtime_dependencies,
+ ApplicationVersion,
+ [ApplicationVersion]},
+ InvalidApplicationVersion :: {invalid_application_version,
+ ApplicationVersion},
+ InvalidAppFile :: {invalid_app_file, Application},
+ Failure :: MissingRuntimeDependencies
+ | InvalidApplicationVersion
+ | InvalidAppFile,
+ Failures :: [Failure].
+
+sanity_check() ->
+ case check_runtime_dependencies() of
+ [] -> ok;
+ Issues -> {failed, Issues}
+ end.
+
%%===================================================================
%% gen_server callbacks
%%===================================================================
@@ -457,6 +484,8 @@ get_application_from_path(Path) ->
{description, proplists:get_value(description, Info, [])},
{vsn, proplists:get_value(vsn, Info, [])},
{path, Path},
+ {runtime_dependencies,
+ proplists:get_value(runtime_dependencies, Info, [])},
{modules, get_modules_from_path(Path)}
]}
end.
@@ -552,3 +581,252 @@ get_beam_name() ->
Value -> Value
end,
Beam ++ Type ++ Flavor.
+
+%% Check runtime dependencies...
+
+vsnstr2vsn(VsnStr) ->
+ list_to_tuple(lists:map(fun (Part) ->
+ list_to_integer(Part)
+ end,
+ string:tokens(VsnStr, "."))).
+
+rtdepstrs2rtdeps([]) ->
+ [];
+rtdepstrs2rtdeps([RTDep | RTDeps]) ->
+ [AppStr, VsnStr] = string:tokens(RTDep, "-"),
+ [{list_to_atom(AppStr), vsnstr2vsn(VsnStr)} | rtdepstrs2rtdeps(RTDeps)].
+
+build_app_table([], AppTab) ->
+ AppTab;
+build_app_table([App | Apps], AppTab0) ->
+ AppTab1 = try
+ %% We may have multiple application versions installed
+ %% of the same application! It is therefore important
+ %% to look up the application version that actually will
+ %% be used via code server.
+ AppFile = code:where_is_file(atom_to_list(App) ++ ".app"),
+ {ok, [{application, App, Info}]} = file:consult(AppFile),
+ VsnStr = proplists:get_value(vsn, Info),
+ Vsn = vsnstr2vsn(VsnStr),
+ RTDepStrs = proplists:get_value(runtime_dependencies,
+ Info, []),
+ RTDeps = rtdepstrs2rtdeps(RTDepStrs),
+ gb_trees:insert(App, {Vsn, RTDeps}, AppTab0)
+ catch
+ _ : _ ->
+ AppTab0
+ end,
+ build_app_table(Apps, AppTab1).
+
+meets_min_req(Vsn, Vsn) ->
+ true;
+meets_min_req({X}, VsnReq) ->
+ meets_min_req({X, 0, 0}, VsnReq);
+meets_min_req({X, Y}, VsnReq) ->
+ meets_min_req({X, Y, 0}, VsnReq);
+meets_min_req(Vsn, {X}) ->
+ meets_min_req(Vsn, {X, 0, 0});
+meets_min_req(Vsn, {X, Y}) ->
+ meets_min_req(Vsn, {X, Y, 0});
+meets_min_req({X, _Y, _Z}, {XReq, _YReq, _ZReq}) when X > XReq ->
+ true;
+meets_min_req({X, Y, _Z}, {X, YReq, _ZReq}) when Y > YReq ->
+ true;
+meets_min_req({X, Y, Z}, {X, Y, ZReq}) when Z > ZReq ->
+ true;
+meets_min_req({_X, _Y, _Z}, {_XReq, _YReq, _ZReq}) ->
+ false;
+meets_min_req(Vsn, VsnReq) ->
+ gp_meets_min_req(mk_gp_vsn_list(Vsn), mk_gp_vsn_list(VsnReq)).
+
+gp_meets_min_req([X, Y, Z | _Vs], [X, Y, Z]) ->
+ true;
+gp_meets_min_req([X, Y, Z | _Vs], [XReq, YReq, ZReq]) ->
+ meets_min_req({X, Y, Z}, {XReq, YReq, ZReq});
+gp_meets_min_req([X, Y, Z | Vs], [X, Y, Z | VReqs]) ->
+ gp_meets_min_req_tail(Vs, VReqs);
+gp_meets_min_req(_Vsn, _VReq) ->
+ %% Versions on different version branches, i.e., the minimum
+ %% required functionality is not included in Vsn.
+ false.
+
+gp_meets_min_req_tail([V | Vs], [V | VReqs]) ->
+ gp_meets_min_req_tail(Vs, VReqs);
+gp_meets_min_req_tail([], []) ->
+ true;
+gp_meets_min_req_tail([_V | _Vs], []) ->
+ true;
+gp_meets_min_req_tail([V | _Vs], [VReq]) when V > VReq ->
+ true;
+gp_meets_min_req_tail(_Vs, _VReqs) ->
+ %% Versions on different version branches, i.e., the minimum
+ %% required functionality is not included in Vsn.
+ false.
+
+mk_gp_vsn_list(Vsn) ->
+ [X, Y, Z | Tail] = tuple_to_list(Vsn),
+ [X, Y, Z | remove_trailing_zeroes(Tail)].
+
+remove_trailing_zeroes([]) ->
+ [];
+remove_trailing_zeroes([0 | Vs]) ->
+ case remove_trailing_zeroes(Vs) of
+ [] -> [];
+ NewVs -> [0 | NewVs]
+ end;
+remove_trailing_zeroes([V | Vs]) ->
+ [V | remove_trailing_zeroes(Vs)].
+
+mk_app_vsn_str({App, Vsn}) ->
+ mk_app_vsn_str(App, Vsn).
+
+mk_app_vsn_str(App, Vsn) ->
+ VsnList = tuple_to_list(Vsn),
+ lists:flatten([atom_to_list(App),
+ $-,
+ integer_to_list(hd(VsnList)),
+ lists:map(fun (Part) ->
+ [$., integer_to_list(Part)]
+ end, tl(VsnList))]).
+
+otp_17_0_vsns_orddict() ->
+ [{asn1,{3,0}},
+ {common_test,{1,8}},
+ {compiler,{5,0}},
+ {cosEvent,{2,1,15}},
+ {cosEventDomain,{1,1,14}},
+ {cosFileTransfer,{1,1,16}},
+ {cosNotification,{1,1,21}},
+ {cosProperty,{1,1,17}},
+ {cosTime,{1,1,14}},
+ {cosTransactions,{1,2,14}},
+ {crypto,{3,3}},
+ {debugger,{4,0}},
+ {dialyzer,{2,7}},
+ {diameter,{1,6}},
+ {edoc,{0,7,13}},
+ {eldap,{1,0,3}},
+ {erl_docgen,{0,3,5}},
+ {erl_interface,{3,7,16}},
+ {erts,{6,0}},
+ {et,{1,5}},
+ {eunit,{2,2,7}},
+ {gs,{1,5,16}},
+ {hipe,{3,10,3}},
+ {ic,{4,3,5}},
+ {inets,{5,10}},
+ {jinterface,{1,5,9}},
+ {kernel,{3,0}},
+ {megaco,{3,17,1}},
+ {mnesia,{4,12}},
+ {observer,{2,0}},
+ {odbc,{2,10,20}},
+ {orber,{3,6,27}},
+ {os_mon,{2,2,15}},
+ {ose,{1,0}},
+ {otp_mibs,{1,0,9}},
+ {parsetools,{2,0,11}},
+ {percept,{0,8,9}},
+ {public_key,{0,22}},
+ {reltool,{0,6,5}},
+ {runtime_tools,{1,8,14}},
+ {sasl,{2,4}},
+ {snmp,{4,25,1}},
+ {ssh,{3,0,1}},
+ {ssl,{5,3,4}},
+ {stdlib,{2,0}},
+ {syntax_tools,{1,6,14}},
+ {test_server,{3,7}},
+ {tools,{2,6,14}},
+ {typer,{0,9,6}},
+ {webtool,{0,8,10}},
+ {wx,{1,2}},
+ {xmerl,{1,3,7}}].
+
+otp_17_0_vsns_tab() ->
+ gb_trees:from_orddict(otp_17_0_vsns_orddict()).
+
+check_runtime_dependency({App, DepVsn}, AppTab) ->
+ case gb_trees:lookup(App, AppTab) of
+ none ->
+ false;
+ {value, {Vsn, _}} ->
+ meets_min_req(Vsn, DepVsn)
+ end.
+
+check_runtime_dependencies(App, AppTab, OtpMinVsnTab) ->
+ case gb_trees:lookup(App, AppTab) of
+ none ->
+ [{invalid_app_file, App}];
+ {value, {Vsn, RTDeps}} ->
+ RTD = case lists:foldl(
+ fun (RTDep, Acc) ->
+ case check_runtime_dependency(RTDep, AppTab) of
+ true ->
+ Acc;
+ false ->
+ [mk_app_vsn_str(RTDep) | Acc]
+ end
+ end,
+ [],
+ RTDeps) of
+ [] ->
+ [];
+ MissingDeps ->
+ [{missing_runtime_dependencies,
+ mk_app_vsn_str(App, Vsn),
+ MissingDeps}]
+ end,
+ case gb_trees:lookup(App, OtpMinVsnTab) of
+ none ->
+ RTD;
+ {value, MinVsn} ->
+ case meets_min_req(Vsn, MinVsn) of
+ true ->
+ RTD;
+ false ->
+ [{invalid_application_version,
+ mk_app_vsn_str(App, Vsn)} | RTD]
+ end
+ end
+ end.
+
+app_file_to_app(AF) ->
+ list_to_atom(filename:basename(AF, ".app")).
+
+get_apps() ->
+ get_apps(code:get_path(), []).
+
+get_apps([], Apps) ->
+ lists:usort(Apps);
+get_apps([Path|Paths], Apps) ->
+ case filelib:wildcard(filename:join(Path, "*.app")) of
+ [] ->
+ %% Not app or invalid app
+ get_apps(Paths, Apps);
+ [AppFile] ->
+ get_apps(Paths, [app_file_to_app(AppFile) | Apps]);
+ [_AppFile| _] = AppFiles ->
+ %% Strange with multple .app files... Lets put them
+ %% all in the list and see what we get...
+ lists:map(fun (AF) ->
+ app_file_to_app(AF)
+ end, AppFiles) ++ Apps
+ end.
+
+check_runtime_dependencies() ->
+ OtpMinVsnTab = otp_17_0_vsns_tab(),
+ Apps = get_apps(),
+ AppTab = build_app_table(Apps, gb_trees:empty()),
+ lists:foldl(fun (App, Acc) ->
+ case check_runtime_dependencies(App,
+ AppTab,
+ OtpMinVsnTab) of
+ [] -> Acc;
+ Issues -> Issues ++ Acc
+ end
+ end,
+ [],
+ Apps).
+
+%% End of runtime dependency checks
diff --git a/lib/runtime_tools/test/system_information_SUITE.erl b/lib/runtime_tools/test/system_information_SUITE.erl
index fb9455a30f..53d20060e7 100644
--- a/lib/runtime_tools/test/system_information_SUITE.erl
+++ b/lib/runtime_tools/test/system_information_SUITE.erl
@@ -33,6 +33,7 @@
api_report/1,
api_to_file/1,
api_from_file/1,
+ sanity_check/1,
%% server
api_start_stop/1,
validate_server_interface/1
@@ -84,7 +85,8 @@ all() -> [
api_to_file,
api_from_file,
api_start_stop,
- validate_server_interface
+ validate_server_interface,
+ sanity_check
].
@@ -262,6 +264,9 @@ validate_server_interface(Config) ->
ok = system_information:stop(),
ok.
+sanity_check(Config) when is_list(Config) ->
+ ok = system_information:sanity_check().
+
%% aux
@@ -288,7 +293,8 @@ validate_report(Report) ->
erts_compile_info,
beam_dynamic_libraries,
environment_erts,
- environment
+ environment,
+ sanity_check
], Report).
ensure_report_keys([], _) -> ok;
diff --git a/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat b/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat
index 0900eadd4a..18938372a3 100644
--- a/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat
+++ b/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat
@@ -9870,4 +9870,5 @@
{"MANPATH",
"/usr/local/man:/usr/share/man:/usr/X11R6/man:/opt/gnome/share/man"},
{"LESSKEY","/etc/lesskey.bin"},
- {"LC_PAPER","sv_SE.UTF-8"}]}]}.
+ {"LC_PAPER","sv_SE.UTF-8"}]},
+ {sanity_check,ok}]}.
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index c282661a61..32953dfc5a 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.8.13
+RUNTIME_TOOLS_VSN = 1.8.14
diff --git a/lib/sasl/doc/src/appup.xml b/lib/sasl/doc/src/appup.xml
index 85fcbed3ba..95f315d269 100644
--- a/lib/sasl/doc/src/appup.xml
+++ b/lib/sasl/doc/src/appup.xml
@@ -4,7 +4,7 @@
<fileref>
<header>
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -84,6 +84,9 @@
version identifier must be specified as a binary, e.g.</p>
<code type="none">&lt;&lt;"2\\.1\\.[0-9]+">></code>
<p>will match all versions <c>2.1.x</c>, where x is any number.</p>
+ <p>Note that the regular expression must match the complete
+ version string, so the above example will work for for
+ e.g. <c>2.1.1</c>, but not for <c>2.1.1.1</c></p>
</section>
<section>
@@ -339,7 +342,7 @@ restart_new_emulator
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 relup, and it shall be placed
- first. <seealso marker="systools#make_relup/3">systools:make_relup3,4</seealso>
+ first. <seealso marker="systools#make_relup/3">systools:make_relup/3,4</seealso>
will ensure this when the relup is generated. The rest of the
relup script is executed after the restart as a part of the boot
script.</p>
@@ -347,11 +350,25 @@ restart_new_emulator
completed. To programatically find out if the upgrade is
complete,
call <seealso marker="release_handler#which_releases/0">
- release_handler:which_releases</seealso> and check if the
+ release_handler:which_releases/0,1</seealso> and check if the
expected release has status <c>current</c>.</p>
<p>The new release must still be made permanent after the upgrade
is completed. Otherwise, the old emulator is started in case of
an emulator restart.</p>
+ <warning>
+ <p>As stated above, the <c>restart_new_emulator</c>
+ instruction causes the emulator to be restarted with new
+ versions of <c>erts</c>, <c>kernel</c>, <c>stdlib</c> and
+ <c>sasl</c>. All other applications, however, will at startup
+ be running their old versions in this new emulator. In most
+ cases this is no problem, but every now and then there will be
+ incompatible changes to the core applications which may cause
+ trouble in this setting. Such incompatible changes (when
+ functions are removed) are normally preceded by a deprecation
+ over two major releases. To make sure your application is not
+ crashed by an incompatible change, always remove any call to
+ deprecated functions as soon as possible.</p>
+ </warning>
<pre>
restart_emulator
</pre>
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index 09d97cbe7b..2928a12d22 100644
--- a/lib/sasl/doc/src/notes.xml
+++ b/lib/sasl/doc/src/notes.xml
@@ -30,6 +30,64 @@
</header>
<p>This document describes the changes made to the SASL application.</p>
+<section><title>SASL 2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The upgrade instruction 'restart_application' would
+ earlier ignore the restart type configured in the .rel
+ file and always restart the application as permanent.
+ This is now changed, and the restart type from the .rel
+ file is used. If restart type is 'load', the application
+ will only be loaded and not started. If the restart type
+ is 'none', the application will not be loaded nor
+ started, but all modules in the application will be
+ loaded. (Thanks to Tobias Schlager for reporting this
+ problem)</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11716</p>
+ </item>
+ <item>
+ <p>
+ If <c>systools:make_script/2</c> failed with reason
+ <c>duplicate_modules</c>, and the <c>silent</c> flag was
+ not used, a crash with reason <c>function_clause</c>
+ would occur when <c>systools</c> tried to format the
+ error message. This has been corrected. (Thanks to
+ Jean-Sébastien Pédron)</p>
+ <p>
+ Own Id: OTP-11819</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Calls to erlang:open_port/2 with 'spawn' are updated to
+ handle space in the command path.</p>
+ <p>
+ Own Id: OTP-10842</p>
+ </item>
+ <item>
+ <p>
+ Some more documentation is added to explain the behavior
+ when an upgrade includes new versions of ERTS, Kernel,
+ STDLIB or SASL.</p>
+ <p>
+ Own Id: OTP-11717</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SASL 2.3.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/sasl/examples/src/target_system.erl b/lib/sasl/examples/src/target_system.erl
index fb9e9aaaaf..a0ae016791 100644
--- a/lib/sasl/examples/src/target_system.erl
+++ b/lib/sasl/examples/src/target_system.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -65,7 +65,7 @@ create(RelFileName,SystoolsOpts) ->
[RelFileName, RelFileName]),
make_script(RelFileName,SystoolsOpts),
- TarFileName = filename:join(Dir,RelFileName ++ ".tar.gz"),
+ TarFileName = RelFileName ++ ".tar.gz",
io:fwrite("Creating tar file ~tp ...~n", [TarFileName]),
make_tar(RelFileName,SystoolsOpts),
@@ -100,6 +100,12 @@ create(RelFileName,SystoolsOpts) ->
copy_file(filename:join([ErtsBinDir, "to_erl"]),
filename:join([TmpBinDir, "to_erl"]), [preserve]),
+ %% This is needed if 'start' script created from 'start.src' shall
+ %% be used as it points out this directory as log dir for 'run_erl'
+ TmpLogDir = filename:join([TmpDir, "log"]),
+ io:fwrite("Creating temporary directory ~tp ...~n", [TmpLogDir]),
+ ok = file:make_dir(TmpLogDir),
+
StartErlDataFile = filename:join([TmpDir, "releases", "start_erl.data"]),
io:fwrite("Creating ~tp ...~n", [StartErlDataFile]),
StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]),
@@ -115,6 +121,7 @@ create(RelFileName,SystoolsOpts) ->
erl_tar:add(Tar, filename:join(TmpDir,ErtsDir), ErtsDir, []),
erl_tar:add(Tar, filename:join(TmpDir,"releases"), "releases", []),
erl_tar:add(Tar, filename:join(TmpDir,"lib"), "lib", []),
+ erl_tar:add(Tar, filename:join(TmpDir,"log"), "log", []),
erl_tar:close(Tar),
%% file:set_cwd(Cwd),
io:fwrite("Removing directory ~tp ...~n",[TmpDir]),
@@ -136,6 +143,11 @@ install(RelFileName, RootDir) ->
subst_src_scripts(["erl", "start", "start_erl"], ErtsBinDir, BinDir,
[{"FINAL_ROOTDIR", RootDir}, {"EMU", "beam"}],
[preserve]),
+ %%! Workaround for pre OTP 17.0: start.src and start_erl.src did
+ %%! not have correct permissions, so the above 'preserve' option did not help
+ ok = file:change_mode(filename:join(BinDir,"start"),8#0755),
+ ok = file:change_mode(filename:join(BinDir,"start_erl"),8#0755),
+
io:fwrite("Creating the RELEASES file ...\n"),
create_RELEASES(RootDir, filename:join([RootDir, "releases",
filename:basename(RelFileName)])).
diff --git a/lib/sasl/src/sasl.app.src b/lib/sasl/src/sasl.app.src
index 8c814cfaf5..8e95197a2a 100644
--- a/lib/sasl/src/sasl.app.src
+++ b/lib/sasl/src/sasl.app.src
@@ -44,5 +44,7 @@
{applications, [kernel, stdlib]},
{env, [{sasl_error_logger, tty},
{errlog_type, all}]},
- {mod, {sasl, []}}]}.
+ {mod, {sasl, []}},
+ {runtime_dependencies, ["tools-2.6.14","stdlib-2.0","kernel-3.0",
+ "erts-6.0"]}]}.
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 3d370a93a5..e5da797efb 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -2233,7 +2233,7 @@ format_error({undefined_applications,Apps}) ->
io_lib:format("Undefined applications: ~p~n",[Apps]);
format_error({duplicate_modules,Dups}) ->
io_lib:format("Duplicated modules: ~n~ts",
- [map(fun({{Mod,_,App1,_,_},{Mod,_,App2,_,_}}) ->
+ [map(fun({{Mod,App1,_},{Mod,App2,_}}) ->
io_lib:format("\t~w specified in ~w and ~w~n",
[Mod,App1,App2])
end, Dups)]);
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index ad2a8005b9..bd7414fbb4 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -667,6 +667,9 @@ release_handler_which_releases(Conf) ->
ok.
+release_handler_which_releases(cleanup,_Conf) ->
+ stop_node(node_name(release_handler_which_releases)).
+
%%-----------------------------------------------------------------
%% Ticket: OTP-2740
%% Slogan: vsn not numeric doesn't work so good in release_handling
@@ -1365,6 +1368,9 @@ upgrade_supervisor(Conf) when is_list(Conf) ->
ok.
+upgrade_supervisor(cleanup,_Condf) ->
+ stop_node(node_name(upgrade_supervisor)).
+
%% Check that if the supervisor fails, then the upgrade is rolled back
%% and an ok error message is returned
upgrade_supervisor_fail(Conf) when is_list(Conf) ->
@@ -1404,18 +1410,41 @@ upgrade_supervisor_fail(Conf) when is_list(Conf) ->
{error,{code_change_failed,_Pid,a_sup,_Vsn,
{error,{invalid_shutdown,brutal_kil}}}} =
- rpc:call(Node, release_handler, install_release, [RelVsn2]),
-
- %% Check that the upgrade is terminated - normally this would mean
- %% rollback, but since this testcase is very simplified the node
- %% is not started with heart supervision and will therefore not be
- %% restarted. So we just check that the node goes down.
+ rpc:call(Node, release_handler, install_release,
+ [RelVsn2, [{error_action,reboot}]]),
+
+ %% Check that the upgrade is terminated - normally this would be a
+ %% rollback, but
+ %%
+ %% 1. Default rollback is done with init:restart(), which does not
+ %% reboot the emulator, it only restarts the system inside the
+ %% running erlang node.
+ %%
+ %% 2. This does not work well on a slave node since, if timing is
+ %% right (bad), the slave node will get the nodedown from its
+ %% master (because distribution is terminated as part of
+ %% init:restart()) and then it will do halt() and thus never be
+ %% restarted (see slave:wloop/1)
+ %%
+ %% 3. Sometimes, though, init:restart() will manage to finish its
+ %% job before the nodedown is received, making the node
+ %% actually restart - in which case it might very well confuse
+ %% the next test case.
+ %%
+ %% 4. So, to avoid unstability we use {error_action,reboot} above,
+ %% to ensure that the node is actually stopped. Of course, in a
+ %% real system this must be used together with heart
+ %% supervision, and then the node will be restarted anyway. But
+ %% here in this simple test case we are satisfied to see that
+ %% the node terminates.
receive {nodedown,Node} -> ok
after 10000 -> ct:fail(failed_upgrade_never_restarted_node)
end,
ok.
+upgrade_supervisor_fail(cleanup,_Condf) ->
+ stop_node(node_name(upgrade_supervisor_fail)).
%% Test upgrade and downgrade of applications
eval_appup(Conf) when is_list(Conf) ->
@@ -2263,8 +2292,8 @@ create_p1g(Conf,TargetDir) ->
ok.
fix_version(SystemLib,App) ->
- FromVsn = vsn(App,current),
- ToVsn = vsn(App,old),
+ FromVsn = re:replace(vsn(App,current),"\\.","\\\\.",[{return,binary}]),
+ ToVsn = re:replace(vsn(App,old),"\\.","\\\\.",[{return,binary}]),
Rootname = filename:join([SystemLib,app_dir(App,old),ebin,atom_to_list(App)]),
AppFile = Rootname ++ ".app",
@@ -2417,9 +2446,28 @@ check_gg_info(Node,OtherAlive,OtherDead,Synced) ->
?t:format("~ncheck_gg_info failed for ~p: ~p~nwhen GGI was: ~p~n"
"and GI was: ~p~n",
[Node,E,GGI,GI]),
+ %% An attempt to find out if it is only a timing issue
+ %% that makes this fail every now and then:
+ try_again_check(Node,GGI,GI,1),
?t:fail("check_gg_info failed")
end.
+try_again_check(_Node,_GGI,_GI,6) ->
+ ok;
+try_again_check(Node,GGI,GI,N) ->
+ timer:sleep(1000),
+ case {rpc:call(Node,global_group,info,[]),
+ rpc:call(Node,global,info,[])} of
+ {GGI,GI} ->
+ ?t:format("~nAfter one more sek, GGI and GI are still the same"),
+ try_again_check(Node,GGI,GI,N+1);
+ {NewGGI,NewGI} ->
+ ?t:format("~nAfter one more sek:~nNew GGI: ~p~nNew GI: ~p~n",
+ [NewGGI,NewGI]),
+ try_again_check(Node,NewGGI,NewGI,N+1)
+ end.
+
+
do_check_gg_info(OtherAlive,OtherDead,Synced,GGI,GI) ->
{_,gg1} = lists:keyfind(own_group_name,1,GGI),
{_,synced} = lists:keyfind(state,1,GGI),
@@ -2563,7 +2611,7 @@ start_nodes(Conf,Snames,Tag) ->
start_node_unix(Sname,NodeDir) ->
Script = filename:join([NodeDir,"bin","start"]),
- ?t:format("Starting ~p: ~tp~n", [Sname,Script]),
+ ?t:format("Starting ~p: ~ts~n", [Sname,Script]),
case rh_test_lib:cmd(Script,[],[{"NODENAME",atom_to_list(Sname)}]) of
ok ->
{ok,node_name(Sname)};
diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl
index f4455f7e9b..e91d220daf 100644
--- a/lib/sasl/test/sasl_SUITE.erl
+++ b/lib/sasl/test/sasl_SUITE.erl
@@ -73,10 +73,10 @@ appup_tests(App,{OkVsns,NokVsns}) ->
ok.
create_test_vsns(App) ->
- This = erlang:system_info(otp_release),
- FirstMajor = previous_major(This),
+ ThisMajor = erlang:system_info(otp_release),
+ FirstMajor = previous_major(ThisMajor),
SecondMajor = previous_major(FirstMajor),
- Ok = app_vsn(App,[FirstMajor]),
+ Ok = app_vsn(App,[ThisMajor,FirstMajor]),
Nok0 = app_vsn(App,[SecondMajor]),
Nok = case Ok of
[Ok1|_] ->
@@ -87,9 +87,9 @@ create_test_vsns(App) ->
{Ok,Nok}.
previous_major("17") ->
- "r16";
-previous_major("r"++Rel) ->
- "r"++previous_major(Rel);
+ "r16b";
+previous_major("r16b") ->
+ "r15b";
previous_major(Rel) ->
integer_to_list(list_to_integer(Rel)-1).
diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl
index e3f6933476..49a4303e0b 100644
--- a/lib/sasl/test/systools_SUITE.erl
+++ b/lib/sasl/test/systools_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -47,6 +47,7 @@
abnormal_script/1, src_tests_script/1, crazy_script/1,
included_script/1, included_override_script/1,
included_fail_script/1, included_bug_script/1, exref_script/1,
+ duplicate_modules_script/1,
otp_3065_circular_dependenies/1, included_and_used_sort_script/1]).
-export([tar_options/1, normal_tar/1, no_mod_vsn_tar/1, system_files_tar/1,
system_files_tar/2, invalid_system_files_tar/1,
@@ -84,6 +85,7 @@ groups() ->
src_tests_script, crazy_script,
included_script, included_override_script,
included_fail_script, included_bug_script, exref_script,
+ duplicate_modules_script,
otp_3065_circular_dependenies, included_and_used_sort_script]},
{tar, [],
[tar_options, normal_tar, no_mod_vsn_tar, system_files_tar,
@@ -822,6 +824,33 @@ no_hipe({ok, Value}) ->
{ok, Value}
end.
+%% duplicate_modules_script: Check that make_script rejects two
+%% applications providing the same module.
+duplicate_modules_script(Config) when is_list(Config) ->
+ {ok, OldDir} = file:get_cwd(),
+
+ {LatestDir, LatestName} = create_script(duplicate_modules,Config),
+
+ DataDir = filename:absname(?copydir),
+
+ ok = file:set_cwd(LatestDir),
+ LibDir = fname([DataDir, d_duplicate_modules, lib]),
+ P = [fname([LibDir, 'app1-1.0', ebin]),
+ fname([LibDir, 'app2-1.0', ebin])],
+
+ %% Check wrong app vsn
+ error = systools:make_script(LatestName, [{path, P}]),
+ {error,
+ systools_make,
+ {duplicate_modules, [
+ {{myapp,app1,_}, {myapp,app2,_}}
+ ]
+ }
+ } = systools:make_script(LatestName, [silent, {path, P}]),
+
+ ok = file:set_cwd(OldDir),
+ ok.
+
%% tar_options: Check illegal tar options.
tar_options(Config) when is_list(Config) ->
{'EXIT',{{badarg,[{path,["Path",12,"Another"]}]}, _}} =
@@ -1586,9 +1615,19 @@ no_sasl_relup(Config) when is_list(Config) ->
%% make_relup: Check that application start type is used in relup
app_start_type_relup(Config) when is_list(Config) ->
+ %% This might fail if some applications are not available, if so
+ %% skip the test case.
+ try create_script(latest_app_start_type2,Config) of
+ {Dir2,Name2} ->
+ app_start_type_relup(Dir2,Name2,Config)
+ catch throw:{error,Reason} ->
+ {skip,Reason}
+ end.
+
+app_start_type_relup(Dir2,Name2,Config) ->
PrivDir = ?config(priv_dir, Config),
{Dir1,Name1} = create_script(latest_app_start_type1,Config),
- {Dir2,Name2} = create_script(latest_app_start_type2,Config),
+
Release1 = filename:join(Dir1,Name1),
Release2 = filename:join(Dir2,Name2),
@@ -2186,7 +2225,10 @@ create_script(current_all_future_sasl,Config) ->
do_create_script(current_all_future_sasl,Config,current,Apps);
create_script({unicode,RelVsn},Config) ->
Apps = core_apps(current) ++ [{ua,"1.0"}],
- do_create_script(unicode,RelVsn,Config,current,Apps).
+ do_create_script(unicode,RelVsn,Config,current,Apps);
+create_script(duplicate_modules,Config) ->
+ Apps = core_apps(current) ++ [{app1,"1.0"},{app2,"1.0"}],
+ do_create_script(duplicate_modules,Config,current,Apps).
do_create_script(Id,Config,ErtsVsn,AppVsns) ->
@@ -2210,9 +2252,13 @@ app_vsns(AppVsns) ->
[{App,app_vsn(App,Vsn)} || {App,Vsn} <- AppVsns] ++
[{App,app_vsn(App,Vsn),Type} || {App,Vsn,Type} <- AppVsns].
app_vsn(App,current) ->
- application:load(App),
- {ok,Vsn} = application:get_key(App,vsn),
- Vsn;
+ case application:load(App) of
+ Ok when Ok==ok; Ok=={error,{already_loaded,App}} ->
+ {ok,Vsn} = application:get_key(App,vsn),
+ Vsn;
+ Error ->
+ throw(Error)
+ end;
app_vsn(_App,Vsn) ->
Vsn.
diff --git a/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/ebin/app1.app b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/ebin/app1.app
new file mode 100644
index 0000000000..dea9257f2f
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/ebin/app1.app
@@ -0,0 +1,7 @@
+{application, app1,
+ [{description, "Application 1"},
+ {vsn, "1.0"},
+ {modules, [myapp]},
+ {registered, []},
+ {applications, []},
+ {env, []}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/src/myapp.erl b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/src/myapp.erl
new file mode 100644
index 0000000000..bf2ab7c79c
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app1-1.0/src/myapp.erl
@@ -0,0 +1,2 @@
+-module(myapp).
+-vsn("1.0").
diff --git a/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/ebin/app2.app b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/ebin/app2.app
new file mode 100644
index 0000000000..476750d8b2
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/ebin/app2.app
@@ -0,0 +1,7 @@
+{application, app2,
+ [{description, "Application 2"},
+ {vsn, "1.0"},
+ {modules, [myapp]},
+ {registered, []},
+ {applications, []},
+ {env, []}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/src/myapp.erl b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/src/myapp.erl
new file mode 100644
index 0000000000..bf2ab7c79c
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_duplicate_modules/lib/app2-1.0/src/myapp.erl
@@ -0,0 +1,2 @@
+-module(myapp).
+-vsn("1.0").
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index ab5514e550..06674095f2 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -33,7 +33,36 @@
</header>
- <section>
+ <section><title>SNMP 4.25.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section>
<title>SNMP Development Toolkit 4.25.0.1</title>
<p>Version 4.25.0.1 supports code replacement in runtime from/to
version 4.25, 4.24.2, 4.24.1 and 4.24. </p>
diff --git a/lib/snmp/doc/src/snmpa_mib_data.xml b/lib/snmp/doc/src/snmpa_mib_data.xml
index c1ea0a91f9..95a33e603e 100644
--- a/lib/snmp/doc/src/snmpa_mib_data.xml
+++ b/lib/snmp/doc/src/snmpa_mib_data.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2013</year><year>2013</year>
+ <year>2013</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/snmp/doc/src/snmpa_mib_storage.xml b/lib/snmp/doc/src/snmpa_mib_storage.xml
index a857ce79e8..791fbc80fe 100644
--- a/lib/snmp/doc/src/snmpa_mib_storage.xml
+++ b/lib/snmp/doc/src/snmpa_mib_storage.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2013</year><year>2013</year>
+ <year>2013</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src
index 904d17954b..cbd292e4c3 100644
--- a/lib/snmp/src/app/snmp.app.src
+++ b/lib/snmp/src/app/snmp.app.src
@@ -136,4 +136,6 @@
%% configuration and use), and in that case mnesia must also be started,
%% before snmp.
{applications, [kernel, stdlib]},
- {mod, {snmp_app, []}}]}.
+ {mod, {snmp_app, []}},
+ {runtime_dependencies, ["stdlib-2.0","runtime_tools-1.8.14","mnesia-4.12",
+ "kernel-3.0","erts-6.0","crypto-3.3"]}]}.
diff --git a/lib/snmp/test/Makefile b/lib/snmp/test/Makefile
index f22b7ea8ee..7bc9dd07d4 100644
--- a/lib/snmp/test/Makefile
+++ b/lib/snmp/test/Makefile
@@ -93,10 +93,10 @@ ifeq ($(SNMP_DEBUG),e)
SNMP_FLAGS += -Dsnmp_error
endif
ifeq ($(SNMP_DEBUG),l)
- SNMP_FLAGS += -Dsnmp_log
+ SNMP_FLAGS += -Dsnmp_error -Dsnmp_log
endif
ifeq ($(SNMP_DEBUG),d)
- SNMP_FLAGS += -Dsnmp_debug
+ SNMP_FLAGS += -Dsnmp_error -Dsnmp_log -Dsnmp_debug
endif
ifeq ($(DONT_USE_TS),true)
diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk
index 3d658bf8e8..fd8315ec4d 100644
--- a/lib/snmp/test/modules.mk
+++ b/lib/snmp/test/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2012. All Rights Reserved.
+# Copyright Ericsson AB 2004-2014. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -42,6 +42,7 @@ TEST_UTIL_MODULES = \
snmp_test_manager \
snmp_test_mgr \
snmp_test_mgr_misc \
+ snmp_test_mgr_counter_server \
sa \
klas3 \
test1 \
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index 89a6ce1253..2a9f2e842d 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -19,10 +19,6 @@
-module(snmp_agent_test).
-%% TODO
-%% * Test fault-tolerance (kill master etc)
-%%
-
-export([
all/0,
groups/0,
@@ -41,7 +37,7 @@
v1_processing/1,
big/1,
big2/1,
- loop_mib/1,
+ loop_mib_1/1,
api/1,
subagent/1,
mnesia/1,
@@ -394,8 +390,9 @@
usm_read/0,
usm_del_user/0,
usm_bad/0,
- loop_mib_1/0,
- loop_mib_2/0,
+ loop_mib_1_test/0,
+ loop_mib_2_test/0,
+ loop_mib_3_test/0,
otp_1129_i/1,
otp_1162_test/0,
otp_1131_test/0,
@@ -546,8 +543,9 @@ groups() ->
init_per_suite(Config0) when is_list(Config0) ->
- ?DBG("init_per_suite -> entry with"
- "~n Config0: ~p", [Config0]),
+ p("init_per_suite -> entry with"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Config0, erlang:nodes()]),
Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0),
Config2 = snmp_test_lib:fix_data_dir(Config1),
@@ -558,16 +556,32 @@ init_per_suite(Config0) when is_list(Config0) ->
Config3 = [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config2],
- ?DBG("init_per_suite -> end with"
- "~n Config3: ~p", [Config3]),
+ snmp_test_mgr_counter_server:start(),
+
+ p("init_per_suite -> end when"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Config3, erlang:nodes()]),
Config3.
end_per_suite(Config) when is_list(Config) ->
- ?DBG("end_per_suite -> entry with"
- "~n Config: ~p", [Config]),
+ p("end_per_suite -> entry with"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Config, erlang:nodes()]),
+
+ case snmp_test_mgr_counter_server:stop() of
+ {ok, _Counters} ->
+ p("end_per_suite -> sucessfully stopped counter server"
+ "~n Counters: ~p", [_Counters]);
+
+ {error, Reason} ->
+ p("end_per_suite -> failed stopping counter server"
+ "~n Reason: ~p", [Reason])
+ end,
+ p("end_per_suite -> end when"
+ "~n Nodes: ~p", [erlang:nodes()]),
Config.
@@ -675,10 +689,16 @@ end_per_group(_GroupName, Config) ->
%% ---- Init Per TestCase ----
init_per_testcase(Case, Config) when is_list(Config) ->
- ?DBG("init_per_testcase -> entry with"
- "~n Config: ~p", [Config]),
+ p("init_per_testcase -> entry with"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Config, erlang:nodes()]),
- init_per_testcase1(Case, Config).
+ Result = init_per_testcase1(Case, Config),
+
+ p("init_per_testcase -> done when"
+ "~n Result: ~p"
+ "~n Nodes: ~p", [Result, erlang:nodes()]),
+ Result.
init_per_testcase1(otp8395 = Case, Config) when is_list(Config) ->
?DBG("init_per_testcase1 -> entry with"
@@ -719,12 +739,18 @@ init_per_testcase1(_Case, Config) when is_list(Config) ->
%% ---- End Per TestCase ----
end_per_testcase(Case, Config) when is_list(Config) ->
- ?DBG("end_per_testcase -> entry with"
- "~n Config: ~p", [Config]),
+ p("end_per_testcase -> entry with"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Config, erlang:nodes()]),
display_log(Config),
- end_per_testcase1(Case, Config).
+ Result = end_per_testcase1(Case, Config),
+
+ p("end_per_testcase -> done with"
+ "~n Result: ~p"
+ "~n Nodes: ~p", [Result, erlang:nodes()]),
+ Result.
end_per_testcase1(otp8395, Config) when is_list(Config) ->
otp8395({fin, Config});
@@ -1173,7 +1199,7 @@ mse_simple(X) -> ?P(mse_simple), simple(X).
mse_v1_processing(X) -> ?P(mse_v1_processing), v1_processing(X).
mse_big(X) -> ?P(mse_big), big(X).
mse_big2(X) -> ?P(mse_big2), big2(X).
-mse_loop_mib(X) -> ?P(mse_loop_mib), loop_mib(X).
+mse_loop_mib(X) -> ?P(mse_loop_mib), loop_mib_1(X).
mse_api(X) -> ?P(mse_api), api(X).
mse_sa_register(X) -> ?P(mse_sa_register), sa_register(X).
mse_v1_trap(X) -> ?P(mse_v1_trap), v1_trap(X).
@@ -1194,7 +1220,7 @@ msd_simple(X) -> ?P(msd_simple), simple(X).
msd_v1_processing(X) -> ?P(msd_v1_processing), v1_processing(X).
msd_big(X) -> ?P(msd_big), big(X).
msd_big2(X) -> ?P(msd_big2), big2(X).
-msd_loop_mib(X) -> ?P(msd_loop_mib), loop_mib(X).
+msd_loop_mib(X) -> ?P(msd_loop_mib), loop_mib_1(X).
msd_api(X) -> ?P(msd_api), api(X).
msd_sa_register(X) -> ?P(msd_sa_register), sa_register(X).
msd_v1_trap(X) -> ?P(msd_v1_trap), v1_trap(X).
@@ -1215,7 +1241,7 @@ msm_simple(X) -> ?P(msm_simple), simple(X).
msm_v1_processing(X) -> ?P(msm_v1_processing), v1_processing(X).
msm_big(X) -> ?P(msm_big2), big(X).
msm_big2(X) -> ?P(msm_loop_mib), big2(X).
-msm_loop_mib(X) -> ?P(msm_loop_mib), loop_mib(X).
+msm_loop_mib(X) -> ?P(msm_loop_mib), loop_mib_1(X).
msm_api(X) -> ?P(msm_api), api(X).
msm_sa_register(X) -> ?P(msm_sa_register), sa_register(X).
msm_v1_trap(X) -> ?P(msm_v1_trap), v1_trap(X).
@@ -1618,7 +1644,7 @@ v1_cases() ->
v1_processing,
big,
big2,
- loop_mib,
+ loop_mib_1,
api,
subagent,
mnesia,
@@ -2095,9 +2121,9 @@ await_dummy_manager_started(Pid) ->
{ok,Pid,Port};
{'EXIT', Pid, Reason} ->
{error, Pid, Reason};
- O ->
+ _O ->
?LOG("dummy_manager_start -> received unknown message:"
- "~n ~p",[O]),
+ "~n ~p",[_O]),
await_dummy_manager_started(Pid)
end.
@@ -2120,16 +2146,16 @@ dummy_manager_send_trap2(Pid) ->
dummy_manager_await_trap2_ack() ->
?DBG("dummy_manager_await_trap2 -> entry",[]),
receive
- {received_trap,Trap} ->
- ?LOG("dummy_manager_await_trap2 -> received trap: ~p",[Trap]),
+ {received_trap, _Trap} ->
+ ?LOG("dummy_manager_await_trap2 -> received trap: ~p", [_Trap]),
%% Note:
%% Without this sleep the v2_inform_i testcase failes! There
%% is no relation between these two test cases as far as I
%% able to figure out...
?SLEEP(60000),
ok;
- O ->
- ?ERR("dummy_manager_await_trap2 -> unexpected message: ~p",[O]),
+ _O ->
+ ?ERR("dummy_manager_await_trap2 -> unexpected message: ~p",[_O]),
ok
after 10000 ->
?ERR("dummy_manager_await_trap2 -> timeout",[]),
@@ -2155,32 +2181,34 @@ dummy_manager_loop(P,S,MA) ->
"~n Trap: ~p",[Trap]),
snmpa:send_trap(MA, Trap, "standard trap"),
dummy_manager_loop(P,S,MA);
- {udp, _UdpId, Ip, UdpPort, Bytes} ->
+ {udp, _UdpId, _Ip, _UdpPort, Bytes} ->
?LOG("dummy_manager_loop -> received upd message"
"~n from: ~p:~p"
"~n size: ~p",
- [Ip, UdpPort, dummy_manager_message_sz(Bytes)]),
+ [_Ip, _UdpPort, dummy_manager_message_sz(Bytes)]),
R = dummy_manager_handle_message(Bytes),
- ?DBG("dummy_manager_loop -> R: ~p",[R]),
+ ?DBG("dummy_manager_loop -> R: ~p", [R]),
P ! R,
- dummy_manager_loop(P,S,MA);
+ dummy_manager_loop(P, S, MA);
stop ->
?DBG("dummy_manager_loop -> received stop request",[]),
P ! {dummy_manager_stopping, self()},
gen_udp:close(S),
exit(normal);
- O ->
+ _O ->
?LOG("dummy_manager_loop -> received unknown message:"
- "~n ~p",[O]),
- dummy_manager_loop(P,S,MA)
+ "~n ~p", [_O]),
+ dummy_manager_loop(P, S, MA)
end.
+-ifdef(snmp_log).
dummy_manager_message_sz(B) when is_binary(B) ->
size(B);
dummy_manager_message_sz(L) when is_list(L) ->
length(L);
dummy_manager_message_sz(_) ->
undefined.
+-endif.
dummy_manager_handle_message(Bytes) ->
case (catch snmp_pdus:dec_message(Bytes)) of
@@ -3398,11 +3426,11 @@ simple_standard_test() ->
db_notify_client(suite) -> [];
db_notify_client(Config) when is_list(Config) ->
?P(db_notify_client),
- {SaNode, MgrNode, MibDir} = init_case(Config),
+ {_SaNode, _MgrNode, _MibDir} = init_case(Config),
?DBG("db_notify_client -> case initiated: "
"~n SaNode: ~p"
"~n MgrNode: ~p"
- "~n MibDir: ~p", [SaNode, MgrNode, MibDir]),
+ "~n MibDir: ~p", [_SaNode, _MgrNode, _MibDir]),
?DBG("db_notify_client -> maximize verbosity", []),
snmpa_local_db:verbosity(trace),
Self = self(),
@@ -4153,8 +4181,8 @@ ma_v2_inform1(MA) ->
CmdExp =
fun(ok) ->
ok;
- ({ok, Val}) ->
- ?DBG("ma_v2_inform -> [cmd2] Val: ~p", [Val]),
+ ({ok, _Val}) ->
+ ?DBG("ma_v2_inform -> [cmd2] Val: ~p", [_Val]),
ok;
({error, Id, Extra}) ->
{error, {unexpected, Id, Extra}};
@@ -4189,10 +4217,10 @@ ma_v2_inform1(MA) ->
CmdSnmpTargets =
fun(T) ->
receive
- {snmp_targets, T, [Addr]} ->
+ {snmp_targets, T, [_Addr]} ->
?DBG("ma_v2_inform1 -> "
"received expected snmp_targets "
- "~n with receiver: ~p",[Addr]),
+ "~n with receiver: ~p", [_Addr]),
ok;
{snmp_targets, T, Addrs} ->
?ERR("ma_v2_inform1 -> "
@@ -4210,16 +4238,16 @@ ma_v2_inform1(MA) ->
Cmd06 =
fun() ->
receive
- {snmp_notification, Tag03, {got_response, Addr}} ->
+ {snmp_notification, Tag03, {got_response, _Addr}} ->
?DBG("ma_v2_inform1 -> "
"received expected snmp_notification "
- "[with manager response] from: ~n ~p",[Addr]),
+ "[with manager response] from: ~n ~p", [_Addr]),
ok;
- {snmp_notification, Tag03, {no_response, Addr}} ->
+ {snmp_notification, Tag03, {no_response, _Addr}} ->
?ERR("ma_v2_inform1 -> "
"received unexpected snmp_notification "
"[without manager response] from: ~n ~p",
- [Addr]),
+ [_Addr]),
{error, no_response}
after
20000 ->
@@ -4249,16 +4277,16 @@ ma_v2_inform1(MA) ->
Cmd10 =
fun() ->
receive
- {snmp_notification, Tag07, {got_response, Addr}} ->
+ {snmp_notification, Tag07, {got_response, _Addr}} ->
?ERR("ma_v2_inform1 -> "
"received unexpected snmp_notification "
- "[with manager response] from: ~n ~p", [Addr]),
+ "[with manager response] from: ~n ~p", [_Addr]),
{error, got_response};
- {snmp_notification, Tag07, {no_response, Addr}} ->
+ {snmp_notification, Tag07, {no_response, _Addr}} ->
?DBG("ma_v2_inform1 -> "
"received expected snmp_notification "
"[without manager response] from: ~n ~p",
- [Addr]),
+ [_Addr]),
ok
after
240000 ->
@@ -4302,8 +4330,8 @@ ma_v2_inform2(MA) ->
CmdExp =
fun(ok) ->
ok;
- ({ok, Val}) ->
- ?DBG("ma_v2_inform -> [cmd2] Val: ~p", [Val]),
+ ({ok, _Val}) ->
+ ?DBG("ma_v2_inform -> [cmd2] Val: ~p", [_Val]),
ok;
({error, Id, Extra}) ->
{error, {unexpected, Id, Extra}};
@@ -4383,8 +4411,8 @@ ma_v2_inform3(MA) ->
"~n send notification: testTrapv22", [MA]),
CmdExpectInform =
- fun(No, Response) ->
- ?DBG("CmdExpectInform -> ~p: ~n~p", [No, Response]),
+ fun(_No, Response) ->
+ ?DBG("CmdExpectInform -> ~p: ~n~p", [_No, Response]),
?expect2({inform, Response},
[{[sysUpTime, 0], any},
{[snmpTrapOID, 0], ?system ++ [0,1]}])
@@ -4393,8 +4421,8 @@ ma_v2_inform3(MA) ->
CmdExp =
fun(ok) ->
ok;
- ({ok, Val}) ->
- ?DBG("CmdExp -> Val: ~p", [Val]),
+ ({ok, _Val}) ->
+ ?DBG("CmdExp -> Val: ~p", [_Val]),
ok;
({error, Id, Extra}) ->
{error, {unexpected, Id, Extra}};
@@ -4505,17 +4533,17 @@ delivery_info(Tag, Address, DeliveryResult, Extra) ->
command_handler([]) ->
ok;
-command_handler([{No, Desc, Cmd}|Rest]) ->
- ?LOG("command_handler -> command ~w: ~n ~s", [No, Desc]),
+command_handler([{_No, _Desc, Cmd}|Rest]) ->
+ ?LOG("command_handler -> command ~w: ~n ~s", [_No, _Desc]),
case (catch Cmd()) of
ok ->
- ?LOG("command_handler -> ~w: ok",[No]),
+ ?LOG("command_handler -> ~w: ok", [_No]),
command_handler(Rest);
{error, Reason} ->
- ?ERR("command_handler -> ~w error: ~n~p",[No, Reason]),
+ ?ERR("command_handler -> ~w error: ~n~p", [_No, Reason]),
?line ?FAIL(Reason);
Error ->
- ?ERR("command_handler -> ~w unexpected: ~n~p",[No, Error]),
+ ?ERR("command_handler -> ~w unexpected: ~n~p", [_No, Error]),
?line ?FAIL({unexpected_command_result, Error})
end.
@@ -5516,57 +5544,59 @@ usm_bad() ->
%% works.
%% Load all std mibs that are not loaded by default.
%%-----------------------------------------------------------------
-loop_mib(suite) -> [];
-loop_mib(Config) when is_list(Config) ->
- ?P(loop_mib),
- ?LOG("loop_mib -> initiate case",[]),
+loop_mib_1(suite) -> [];
+loop_mib_1(Config) when is_list(Config) ->
+ ?P(loop_mib_1),
+ ?LOG("loop_mib_1 -> initiate case",[]),
%% snmpa:verbosity(master_agent,debug),
%% snmpa:verbosity(mib_server,info),
- {SaNode, MgrNode, MibDir} = init_case(Config),
- ?DBG("loop_mib -> ~n"
+ {_SaNode, _MgrNode, _MibDir} = init_case(Config),
+ ?DBG("loop_mib_1 -> ~n"
"\tSaNode: ~p~n"
"\tMgrNode: ~p~n"
- "\tMibDir: ~p",[SaNode, MgrNode, MibDir]),
- ?DBG("loop_mib -> load mib SNMP-COMMUNITY-MIB",[]),
+ "\tMibDir: ~p", [_SaNode, _MgrNode, _MibDir]),
+ ?DBG("loop_mib_1 -> load mib SNMP-COMMUNITY-MIB",[]),
?line load_master_std("SNMP-COMMUNITY-MIB"),
- ?DBG("loop_mib -> load mib SNMP-MPD-MIB",[]),
+ ?DBG("loop_mib_1 -> load mib SNMP-MPD-MIB",[]),
?line load_master_std("SNMP-MPD-MIB"),
- ?DBG("loop_mib -> load mib SNMP-TARGET-MIB",[]),
+ ?DBG("loop_mib_1 -> load mib SNMP-TARGET-MIB",[]),
?line load_master_std("SNMP-TARGET-MIB"),
- ?DBG("loop_mib -> load mib SNMP-NOTIFICATION-MIB",[]),
+ ?DBG("loop_mib_1 -> load mib SNMP-NOTIFICATION-MIB",[]),
?line load_master_std("SNMP-NOTIFICATION-MIB"),
- ?DBG("loop_mib -> load mib SNMP-FRAMEWORK-MIB",[]),
+ ?DBG("loop_mib_1 -> load mib SNMP-FRAMEWORK-MIB",[]),
?line load_master_std("SNMP-FRAMEWORK-MIB"),
- ?DBG("loop_mib -> load mib SNMP-VIEW-BASED-ACM-MIB",[]),
+ ?DBG("loop_mib_1 -> load mib SNMP-VIEW-BASED-ACM-MIB",[]),
?line load_master_std("SNMP-VIEW-BASED-ACM-MIB"),
- ?DBG("loop_mib -> try",[]),
- try_test(loop_mib_1),
- ?DBG("loop_mib -> unload mib SNMP-COMMUNITY-MIB",[]),
+ ?DBG("loop_mib_1 -> try",[]),
+
+ try_test(loop_mib_1_test),
+
+ ?DBG("loop_mib_1 -> unload mib SNMP-COMMUNITY-MIB",[]),
?line unload_master("SNMP-COMMUNITY-MIB"),
- ?DBG("loop_mib -> unload mib SNMP-MPD-MIB",[]),
+ ?DBG("loop_mib_1 -> unload mib SNMP-MPD-MIB",[]),
?line unload_master("SNMP-MPD-MIB"),
- ?DBG("loop_mib -> unload mib SNMP-TARGET-MIB",[]),
+ ?DBG("loop_mib_1 -> unload mib SNMP-TARGET-MIB",[]),
?line unload_master("SNMP-TARGET-MIB"),
- ?DBG("loop_mib -> unload mib SNMP-NOTIFICATION-MIB",[]),
+ ?DBG("loop_mib_1 -> unload mib SNMP-NOTIFICATION-MIB",[]),
?line unload_master("SNMP-NOTIFICATION-MIB"),
- ?DBG("loop_mib -> unload mib SNMP-FRAMEWORK-MIB",[]),
+ ?DBG("loop_mib_1 -> unload mib SNMP-FRAMEWORK-MIB",[]),
?line unload_master("SNMP-FRAMEWORK-MIB"),
- ?DBG("loop_mib -> unload mib SNMP-VIEW-BASED-ACM-MIB",[]),
+ ?DBG("loop_mib_1 -> unload mib SNMP-VIEW-BASED-ACM-MIB",[]),
?line unload_master("SNMP-VIEW-BASED-ACM-MIB"),
%% snmpa:verbosity(master_agent,log),
%% snmpa:verbosity(mib_server,silence),
- ?LOG("loop_mib -> done",[]).
+ ?LOG("loop_mib_1 -> done",[]).
loop_mib_2(suite) -> [];
loop_mib_2(Config) when is_list(Config) ->
?P(loop_mib_2),
?LOG("loop_mib_2 -> initiate case",[]),
- {SaNode, MgrNode, MibDir} = init_case(Config),
- ?DBG("loop_mib_2 -> ~n"
+ {_SaNode, _MgrNode, _MibDir} = init_case(Config),
+ ?DBG("do_loop_mib_2 -> ~n"
"\tSaNode: ~p~n"
"\tMgrNode: ~p~n"
- "\tMibDir: ~p",[SaNode, MgrNode, MibDir]),
+ "\tMibDir: ~p", [_SaNode, _MgrNode, _MibDir]),
?DBG("loop_mib_2 -> load mibs",[]),
?line load_master_std("SNMP-COMMUNITY-MIB"),
?line load_master_std("SNMP-MPD-MIB"),
@@ -5574,7 +5604,9 @@ loop_mib_2(Config) when is_list(Config) ->
?line load_master_std("SNMP-NOTIFICATION-MIB"),
?line load_master_std("SNMP-FRAMEWORK-MIB"),
?line load_master_std("SNMP-VIEW-BASED-ACM-MIB"),
- try_test(loop_mib_2),
+
+ try_test(loop_mib_2_test),
+
?DBG("loop_mib_2 -> unload mibs",[]),
?line unload_master("SNMP-COMMUNITY-MIB"),
?line unload_master("SNMP-MPD-MIB"),
@@ -5589,18 +5621,18 @@ loop_mib_3(suite) -> [];
loop_mib_3(Config) when is_list(Config) ->
?P(loop_mib_3),
?LOG("loop_mib_3 -> initiate case",[]),
- {SaNode, MgrNode, MibDir} = init_case(Config),
+ {_SaNode, _MgrNode, _MibDir} = init_case(Config),
?DBG("loop_mib_3 -> ~n"
"\tSaNode: ~p~n"
"\tMgrNode: ~p~n"
- "\tMibDir: ~p",[SaNode, MgrNode, MibDir]),
+ "\tMibDir: ~p", [_SaNode, _MgrNode, _MibDir]),
?DBG("loop_mib_3 -> load mibs",[]),
?line load_master_std("SNMP-TARGET-MIB"),
?line load_master_std("SNMP-NOTIFICATION-MIB"),
?line load_master_std("SNMP-VIEW-BASED-ACM-MIB"),
?line load_master_std("SNMP-USER-BASED-SM-MIB"),
- try_test(loop_mib_2),
+ try_test(loop_mib_3_test),
?DBG("loop_mib_3 -> unload mibs",[]),
?line unload_master("SNMP-TARGET-MIB"),
@@ -5611,17 +5643,16 @@ loop_mib_3(Config) when is_list(Config) ->
%% Req. As many mibs all possible
-loop_mib_1() ->
- ?DBG("loop_mib_1 -> entry",[]),
+loop_mib_1_test() ->
+ ?DBG("loop_mib_1_test -> entry",[]),
N = loop_it_1([1,1], 0),
io:format(user, "found ~w varibles\n", [N]),
?line N = if N < 100 -> 100;
true -> N
end.
-
loop_it_1(Oid, N) ->
- ?DBG("loop_it_1 -> entry with~n"
+ ?DBG("loop_it_1_test -> entry with~n"
"\tOid: ~p~n"
"\tN: ~p",[Oid,N]),
case get_next_req([Oid]) of
@@ -5629,13 +5660,13 @@ loop_it_1(Oid, N) ->
error_status = noError,
error_index = 0,
varbinds = [#varbind{oid = NOid,
- value = Value}]} when NOid > Oid ->
- ?DBG("loop_it_1 -> "
+ value = _Value}]} when NOid > Oid ->
+ ?DBG("loop_it_1_test -> "
"~n NOid: ~p"
- "~n Value: ~p",[NOid, Value]),
- ?line [Value2] = get_req(1, [NOid]), % must not be same
- ?DBG("loop_it_1 -> "
- "~n Value2: ~p",[Value2]),
+ "~n Value: ~p", [NOid, _Value]),
+ ?line [_Value2] = get_req(1, [NOid]), % must not be same
+ ?DBG("loop_it_1_test -> "
+ "~n Value2: ~p", [_Value2]),
loop_it_1(NOid, N+1);
#pdu{type = 'get-response',
@@ -5648,7 +5679,7 @@ loop_it_1(Oid, N) ->
error_status = noSuchName,
error_index = 1,
varbinds = [_]} ->
- ?DBG("loop_it_1 -> done: ~p",[N]),
+ ?DBG("loop_it_1_test -> done: ~p",[N]),
N;
#pdu{type = 'get-response',
@@ -5669,14 +5700,13 @@ loop_it_1(Oid, N) ->
%% Req. As many mibs all possible
-loop_mib_2() ->
- ?DBG("loop_mib_1 -> entry",[]),
+loop_mib_2_test() ->
+ ?DBG("loop_mib_2_test -> entry",[]),
N = loop_it_2([1,1], 0),
io:format(user, "found ~w varibles\n", [N]),
?line N = if N < 100 -> 100;
true -> N
end.
-
loop_it_2(Oid, N) ->
?DBG("loop_it_2 -> entry with"
@@ -5686,22 +5716,22 @@ loop_it_2(Oid, N) ->
#pdu{type = 'get-response',
error_status = noError,
error_index = 0,
- varbinds = [#varbind{oid = NOid, value = endOfMibView}]} ->
+ varbinds = [#varbind{oid = _NOid, value = endOfMibView}]} ->
?DBG("loop_it_2 -> "
- "~n NOid: ~p",[NOid]),
+ "~n NOid: ~p", [_NOid]),
N;
#pdu{type = 'get-response',
error_status = noError,
error_index = 0,
varbinds = [#varbind{oid = NOid,
- value = Value}]} when NOid > Oid ->
+ value = _Value}]} when NOid > Oid ->
?DBG("loop_it_2 -> "
"~n NOid: ~p"
- "~n Value: ~p",[NOid, Value]),
- ?line [Value2] = get_req(1, [NOid]), % must not be same
+ "~n Value: ~p", [NOid, _Value]),
+ ?line [_Value2] = get_req(1, [NOid]), % must not be same
?DBG("loop_it_2 -> "
- "~n Value2: ~p",[Value2]),
+ "~n Value2: ~p", [_Value2]),
loop_it_2(NOid, N+1);
#pdu{type = 'get-response',
@@ -5744,6 +5774,10 @@ loop_it_2(Oid, N) ->
end.
+loop_mib_3_test() ->
+ ?DBG("loop_mib_3_test -> entry",[]),
+ loop_mib_2_test().
+
%%%-----------------------------------------------------------------
%%% Testing of reported bugs and other tickets.
@@ -6611,16 +6645,16 @@ otp8395(Config) when is_list(Config) ->
AgentNode = ?config(agent_node, Config),
AgentLogDir = ?config(agent_log_dir, Config),
OutFile = join([AgentLogDir, "otp8395.txt"]),
- {ok, LogInfo} = rpc:call(AgentNode, snmpa, log_info, []),
- ?DBG("otp8395 -> LogInfo: ~p", [LogInfo]),
+ {ok, _LogInfo} = rpc:call(AgentNode, snmpa, log_info, []),
+ ?DBG("otp8395 -> LogInfo: ~p", [_LogInfo]),
%% SyncRes = rpc:call(AgentNode, snmp, log_sync, [?audit_trail_log_name]),
%% ?DBG("otp8395 -> SyncRes: ~p", [SyncRes]),
ok = agent_log_validation(AgentNode),
- LTTRes =
+ _LTTRes =
rpc:call(AgentNode, snmpa, log_to_txt, [AgentLogDir, [], OutFile]),
- ?DBG("otp8395 -> LTTRes: ~p", [LTTRes]),
+ ?DBG("otp8395 -> LTTRes: ~p", [_LTTRes]),
?SLEEP(1000),
?DBG("otp8395 -> done", []),
@@ -6941,10 +6975,10 @@ stop_stdalone_agent(Pid) when (node(Pid) =/= node()) ->
MRef = erlang:monitor(process, Pid),
rpc:call(node(Pid), ?MODULE, stop_stdalone_agent, [Pid]),
receive
- {'DOWN', MRef, process, Pid, Info} ->
+ {'DOWN', MRef, process, Pid, _Info} ->
?DBG("received expected DOWN message "
"regarding snmp agent supervisor: "
- "~n Info: ~p", [Info]),
+ "~n Info: ~p", [_Info]),
ok
after 5000 ->
?DBG("no DOWN message "
@@ -7003,9 +7037,9 @@ do_info(MaNode) ->
tree_size_bytes,
db_memory]}],
verify_info(Info, Keys),
- OldInfo = snmpa:old_info_format(Info),
- ?DBG("info_test1 -> OldInfo: ~n~p", [OldInfo]),
- verify_old_info(OldInfo),
+ %% OldInfo = snmpa:old_info_format(Info),
+ %% ?DBG("info_test1 -> OldInfo: ~n~p", [OldInfo]),
+ %% verify_old_info(OldInfo),
ok.
verify_info([], []) ->
@@ -7048,20 +7082,20 @@ verify_subinfo(Info0, [Key|Keys]) ->
verify_subinfo(Info, Keys)
end.
-verify_old_info(Info) ->
- Keys = [vsns, subagents, loaded_mibs,
- tree_size_bytes, process_memory, db_memory],
- verify_old_info(Keys, Info).
-
-verify_old_info([], _) ->
- ok;
-verify_old_info([Key|Keys], Info) ->
- case lists:keymember(Key, 1, Info) of
- true ->
- verify_old_info(Keys, Info);
- false ->
- ?FAIL({missing_old_info, Key})
- end.
+%% verify_old_info(Info) ->
+%% Keys = [vsns, subagents, loaded_mibs,
+%% tree_size_bytes, process_memory, db_memory],
+%% verify_old_info(Keys, Info).
+
+%% verify_old_info([], _) ->
+%% ok;
+%% verify_old_info([Key|Keys], Info) ->
+%% case lists:keymember(Key, 1, Info) of
+%% true ->
+%% verify_old_info(Keys, Info);
+%% false ->
+%% ?FAIL({missing_old_info, Key})
+%% end.
%% Index String - string used in index
is(S) -> [length(S) | S].
diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl
index 122289c28e..d7109253f7 100644
--- a/lib/snmp/test/snmp_agent_test_lib.erl
+++ b/lib/snmp/test/snmp_agent_test_lib.erl
@@ -299,10 +299,10 @@ call(N,M,F,A) ->
"~n Loc: ~p", [Rn, Loc]),
put(test_server_loc, Loc),
exit(Rn);
- {done, Ret, Zed} ->
+ {done, Ret, _Zed} ->
?DBG("call -> done:"
"~n Ret: ~p"
- "~n Zed: ~p", [Ret, Zed]),
+ "~n Zed: ~p", [Ret, _Zed]),
case Ret of
{error, Reason} ->
exit(Reason);
@@ -338,8 +338,8 @@ run(Mod, Func, Args, Opts) ->
CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID),
Community = snmp_misc:get_option(community, Opts, "all-rights"),
?DBG("run -> start crypto app",[]),
- Crypto = ?CRYPTO_START(),
- ?DBG("run -> Crypto: ~p", [Crypto]),
+ _CryptoRes = ?CRYPTO_START(),
+ ?DBG("run -> Crypto: ~p", [_CryptoRes]),
catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case
StdM = join(code:priv_dir(snmp), "mibs") ++ "/",
Vsn = get(vsn),
@@ -676,9 +676,9 @@ stop_agent(Config) when is_list(Config) ->
(catch process_info(Sup)),
(catch process_info(Par))]),
- Info = agent_info(Sup),
+ _Info = agent_info(Sup),
?DBG("stop_agent -> Agent info: "
- "~n ~p", [Info]),
+ "~n ~p", [_Info]),
stop_sup(Sup, Par),
@@ -1303,10 +1303,10 @@ get_req(Id, Vars) ->
{ok, Val} ->
?DBG("get_req -> response: ~p",[Val]),
Val;
- {error, _, {ExpFmt, ExpArg}, {ActFmt, ActArg}} ->
+ {error, _, {_ExpFmt, ExpArg}, {_ActFmt, ActArg}} ->
?DBG("get_req -> error for ~p: "
- "~n " ++ ExpFmt ++
- "~n " ++ ActFmt,
+ "~n " ++ _ExpFmt ++
+ "~n " ++ _ActFmt,
[Id] ++ ExpArg ++ ActArg),
exit({unexpected_response, ExpArg, ActArg});
Error ->
@@ -1527,9 +1527,9 @@ rewrite_target_addr_conf(Dir, NewPort) ->
case file:read_file_info(TAFile) of
{ok, _} ->
ok;
- {error, R} ->
+ {error, _R} ->
?ERR("failure reading file info of "
- "target address config file: ~p",[R]),
+ "target address config file: ~p", [_R]),
ok
end,
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index 5fe18980bc..3a654a2805 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -139,6 +139,8 @@
-define(NS_TIMEOUT, 10000).
+-define(DEFAULT_MNESIA_DEBUG, none).
+
%%----------------------------------------------------------------------
%% Records
@@ -173,7 +175,9 @@ end_per_suite(Config) when is_list(Config) ->
init_per_testcase(Case, Config) when is_list(Config) ->
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]),
+ io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
+ p(Case, "init_per_testcase begin when"
+ "~n Nodes: ~p~n~n", [erlang:nodes()]),
%% This version of the API, based on Addr and Port, has been deprecated
DeprecatedApiCases =
[
@@ -187,16 +191,25 @@ init_per_testcase(Case, Config) when is_list(Config) ->
simple_async_get_bulk1,
misc_async1
],
- case lists:member(Case, DeprecatedApiCases) of
- true ->
- %% ?SKIP(api_no_longer_supported);
- {skip, api_no_longer_supported};
- false ->
- init_per_testcase2(Case, Config)
- end.
+ Result =
+ case lists:member(Case, DeprecatedApiCases) of
+ true ->
+ %% ?SKIP(api_no_longer_supported);
+ {skip, api_no_longer_supported};
+ false ->
+ init_per_testcase2(Case, Config)
+ end,
+ p(Case, "init_per_testcase end when"
+ "~n Nodes: ~p"
+ "~n Result: ~p"
+ "~n~n", [Result, erlang:nodes()]),
+ Result.
init_per_testcase2(Case, Config) ->
- ?DBG("init_per_testcase2 -> ~p", [erlang:nodes()]),
+ ?DBG("init_per_testcase2 -> "
+ "~n Case: ~p"
+ "~n Config: ~p"
+ "~n Nodes: ~p", [Case, Config, erlang:nodes()]),
CaseTopDir = snmp_test_lib:init_testcase_top_dir(Case, Config),
@@ -314,6 +327,8 @@ init_per_testcase3(Case, Config) ->
end.
end_per_testcase(Case, Config) when is_list(Config) ->
+ p(Case, "end_per_testcase begin when"
+ "~n Nodes: ~p~n~n", [erlang:nodes()]),
?DBG("fin [~w] Nodes [1]: ~p", [Case, erlang:nodes()]),
Dog = ?config(watchdog, Config),
?WD_STOP(Dog),
@@ -322,6 +337,8 @@ end_per_testcase(Case, Config) when is_list(Config) ->
?DBG("fin [~w] Nodes [2]: ~p", [Case, erlang:nodes()]),
%% TopDir = ?config(top_dir, Conf2),
%% ?DEL_DIR(TopDir),
+ p(Case, "end_per_testcase end when"
+ "~n Nodes: ~p~n~n", [erlang:nodes()]),
Conf2.
end_per_testcase2(Case, Config) ->
@@ -428,10 +445,10 @@ groups() ->
{request_tests, [],
[
{group, get_tests},
- {group, get_next_tests},
+ {group, get_next_tests},
{group, set_tests},
- {group, bulk_tests},
- {group, misc_request_tests}
+ {group, bulk_tests},
+ {group, misc_request_tests}
]
},
{request_tests_mt, [],
@@ -5303,34 +5320,59 @@ init_manager(AutoInform, Config) ->
?line Node = start_manager_node(),
+ %% The point with this (try catch block) is to be
+ %% able to do some cleanup in case we fail to
+ %% start some of the apps. That is, if we fail to
+ %% start the apps (mnesia, crypto and snmp agent)
+ %% we stop the (agent) node!
- %% --
- %% Start and initiate crypto on manager node
- %%
-
- ?line ok = init_crypto(Node),
+ try
+ begin
- %%
- %% Write manager config
- %%
+ %% --
+ %% Start and initiate crypto on manager node
+ %%
+
+ ?line ok = init_crypto(Node),
+
+ %%
+ %% Write manager config
+ %%
+
+ ?line ok = write_manager_config(Config),
+
+ IRB = case AutoInform of
+ true ->
+ auto;
+ _ ->
+ user
+ end,
+ Conf = [{manager_node, Node}, {irb, IRB} | Config],
+ Vsns = [v1,v2,v3],
+ start_manager(Node, Vsns, Conf)
+ end
+ catch
+ T:E ->
+ StackTrace = ?STACK(),
+ p("Failure during manager start: "
+ "~n Error Type: ~p"
+ "~n Error: ~p"
+ "~n StackTrace: ~p", [T, E, StackTrace]),
+ %% And now, *try* to cleanup
+ (catch stop_node(Node)),
+ ?FAIL({failed_starting_manager, T, E, StackTrace})
+ end.
- ?line ok = write_manager_config(Config),
-
- IRB = case AutoInform of
- true ->
- auto;
- _ ->
- user
- end,
- Conf = [{manager_node, Node}, {irb, IRB} | Config],
- Vsns = [v1,v2,v3],
- start_manager(Node, Vsns, Conf).
-
fin_manager(Config) ->
Node = ?config(manager_node, Config),
- stop_manager(Node, Config),
- fin_crypto(Node),
- stop_node(Node),
+ StopMgrRes = stop_manager(Node),
+ StopCryptoRes = fin_crypto(Node),
+ StopNode = stop_node(Node),
+ p("fin_agent -> stop apps and (mgr node ~p) node results: "
+ "~n SNMP Mgr: ~p"
+ "~n Crypto: ~p"
+ "~n Node: ~p",
+ [Node, StopMgrRes, StopCryptoRes, StopNode]),
Config.
@@ -5352,52 +5394,93 @@ init_agent(Config) ->
?line Node = start_agent_node(),
+ %% The point with this (try catch block) is to be
+ %% able to do some cleanup in case we fail to
+ %% start some of the apps. That is, if we fail to
+ %% start the apps (mnesia, crypto and snmp agent)
+ %% we stop the (agent) node!
- %% --
- %% Start and initiate mnesia on agent node
- %%
-
- ?line ok = init_mnesia(Node, Dir),
-
-
- %% --
- %% Start and initiate crypto on agent node
- %%
-
- ?line ok = init_crypto(Node),
-
-
- %%
- %% Write agent config
- %%
-
- Vsns = [v1,v2],
- ?line ok = write_agent_config(Vsns, Config),
-
- Conf = [{agent_node, Node},
- {mib_dir, MibDir} | Config],
+ try
+ begin
+
+ %% --
+ %% Start and initiate mnesia on agent node
+ %%
+
+ ?line ok = init_mnesia(Node, Dir, ?config(mnesia_debug, Config)),
+
+
+ %% --
+ %% Start and initiate crypto on agent node
+ %%
+
+ ?line ok = init_crypto(Node),
+
+
+ %%
+ %% Write agent config
+ %%
+
+ Vsns = [v1,v2],
+ ?line ok = write_agent_config(Vsns, Config),
+
+ Conf = [{agent_node, Node},
+ {mib_dir, MibDir} | Config],
- %%
- %% Start the agent
- %%
-
- start_agent(Node, Vsns, Conf).
+ %%
+ %% Start the agent
+ %%
+
+ start_agent(Node, Vsns, Conf)
+ end
+ catch
+ T:E ->
+ StackTrace = ?STACK(),
+ p("Failure during agent start: "
+ "~n Error Type: ~p"
+ "~n Error: ~p"
+ "~n StackTrace: ~p", [T, E, StackTrace]),
+ %% And now, *try* to cleanup
+ (catch stop_node(Node)),
+ ?FAIL({failed_starting_agent, T, E, StackTrace})
+ end.
+
fin_agent(Config) ->
Node = ?config(agent_node, Config),
- stop_agent(Node, Config),
- fin_crypto(Node),
- fin_mnesia(Node),
- stop_node(Node),
+ StopAgentRes = stop_agent(Node),
+ StopCryptoRes = fin_crypto(Node),
+ StopMnesiaRes = fin_mnesia(Node),
+ StopNode = stop_node(Node),
+ p("fin_agent -> stop apps and (agent node ~p) node results: "
+ "~n SNMP Agent: ~p"
+ "~n Crypto: ~p"
+ "~n Mnesia: ~p"
+ "~n Node: ~p",
+ [Node, StopAgentRes, StopCryptoRes, StopMnesiaRes, StopNode]),
Config.
-init_mnesia(Node, Dir) ->
+init_mnesia(Node, Dir, MnesiaDebug)
+ when ((MnesiaDebug =/= none) andalso
+ (MnesiaDebug =/= debug) andalso (MnesiaDebug =/= trace)) ->
+ init_mnesia(Node, Dir, ?DEFAULT_MNESIA_DEBUG);
+init_mnesia(Node, Dir, MnesiaDebug) ->
?DBG("init_mnesia -> load application mnesia", []),
?line ok = load_mnesia(Node),
?DBG("init_mnesia -> application mnesia: set_env dir: ~n~p",[Dir]),
?line ok = set_mnesia_env(Node, dir, filename:join(Dir, "mnesia")),
+ %% Just in case, only set (known to be) valid values for debug
+ if
+ ((MnesiaDebug =:= debug) orelse (MnesiaDebug =:= trace)) ->
+ ?DBG("init_mnesia -> application mnesia: set_env debug: ~w",
+ [MnesiaDebug]),
+ ?line ok = set_mnesia_env(Node, debug, MnesiaDebug);
+ true ->
+ ok
+ end,
+
?DBG("init_mnesia -> create mnesia schema",[]),
?line case create_schema(Node) of
ok ->
@@ -5434,25 +5517,89 @@ fin_crypto(Node) ->
%% -- Misc application wrapper functions --
-load_app(Node, App) when (Node =:= node()) andalso is_atom(App) ->
- application:load(App);
-load_app(Node, App) when is_atom(App) ->
- rcall(Node, application, load, [App]).
-
-start_app(Node, App) when (Node =:= node()) andalso is_atom(App) ->
- application:start(App);
+load_app(Node, App) ->
+ VerifySuccess = fun(ok) ->
+ ok;
+ ({error, {already_loaded, LoadedApp}}) when (LoadedApp =:= App) ->
+ ok;
+ ({error, Reason}) ->
+ p("failed loading app ~w on ~p: "
+ "~n ~p", [App, Node, Reason]),
+ ?FAIL({failed_load, Node, App, Reason})
+ end,
+ do_load_app(Node, App, VerifySuccess).
+
+do_load_app(Node, App, VerifySuccess)
+ when (Node =:= node()) andalso is_atom(App) ->
+ %% Local app
+ exec(fun() -> application:load(App) end, VerifySuccess);
+do_load_app(Node, App, VerifySuccess) ->
+ %% Remote app
+ exec(fun() -> rcall(Node, application, load, [App]) end, VerifySuccess).
+
+
start_app(Node, App) ->
- rcall(Node, application, start, [App]).
+ VerifySuccess = fun(ok) ->
+ ok;
+ ({error, {already_started, LoadedApp}}) when (LoadedApp =:= App) ->
+ ok;
+ ({error, Reason}) ->
+ p("failed starting app ~w on ~p: "
+ "~n ~p", [App, Node, Reason]),
+ ?FAIL({failed_start, Node, App, Reason})
+ end,
+ start_app(Node, App, VerifySuccess).
+
+start_app(Node, App, VerifySuccess)
+ when (Node =:= node()) andalso is_atom(App) ->
+ exec(fun() -> application:start(App) end, VerifySuccess);
+start_app(Node, App, VerifySuccess) ->
+ exec(fun() -> rcall(Node, application, start, [App]) end, VerifySuccess).
+
+
+stop_app(Node, App) ->
+ VerifySuccess = fun(ok) ->
+ ok;
+ ({error, {not_started, LoadedApp}}) when (LoadedApp =:= App) ->
+ ok;
+ ({error, Reason}) ->
+ p("failed stopping app ~w on ~p: "
+ "~n ~p", [App, Node, Reason]),
+ ?FAIL({failed_stop, Node, App, Reason})
+ end,
+ stop_app(Node, App, VerifySuccess).
+
+stop_app(Node, App, VerifySuccess)
+ when (Node =:= node()) andalso is_atom(App) ->
+ exec(fun() -> application:stop(App) end, VerifySuccess);
+stop_app(Node, App, VerifySuccess) when is_atom(App) ->
+ exec(fun() -> rcall(Node, application, stop, [App]) end, VerifySuccess).
+
+
+set_app_env(Node, App, Key, Val) ->
+ VerifySuccess = fun(ok) ->
+ ok;
+ ({error, Reason}) ->
+ p("failed setting app ~w env on ~p"
+ "~n Key: ~p"
+ "~n Val: ~p"
+ "~n Reason: ~p"
+ "~n ~p", [App, Node, Key, Val, Reason]),
+ ?FAIL({failed_set_app_env,
+ Node, App, Key, Val, Reason})
+ end,
+ set_app_env(Node, App, Key, Val, VerifySuccess).
-stop_app(Node, App) when (Node =:= node()) andalso is_atom(App) ->
- application:stop(App);
-stop_app(Node, App) when is_atom(App) ->
- rcall(Node, application, stop, [App]).
+set_app_env(Node, App, Key, Val, VerifySuccess)
+ when (Node =:= node()) andalso is_atom(App) ->
+ exec(fun() -> application:set_env(App, Key, Val) end, VerifySuccess);
+set_app_env(Node, App, Key, Val, VerifySuccess) when is_atom(App) ->
+ exec(fun() -> rcall(Node, application, set_env, [App, Key, Val]) end,
+ VerifySuccess).
-set_app_env(Node, App, Key, Val) when (Node =:= node()) andalso is_atom(App) ->
- application:set_env(App, Key, Val);
-set_app_env(Node, App, Key, Val) when is_atom(App) ->
- rcall(Node, application, set_env, [App, Key, Val]).
+
+exec(Cmd, VerifySuccess) ->
+ VerifySuccess(Cmd()).
%% -- Misc snmp wrapper functions --
@@ -5900,9 +6047,9 @@ start_manager(Node, Vsns, Conf0, _Opts) ->
Conf0.
-stop_manager(Node, Conf) ->
- stop_snmp(Node),
- Conf.
+stop_manager(Node) ->
+ stop_snmp(Node).
+
%% -- Misc agent wrapper functions --
@@ -5951,9 +6098,8 @@ start_agent(Node, Vsns, Conf0, _Opts) ->
?line ok = start_snmp(Node),
Conf0.
-stop_agent(Node, Conf) ->
- stop_snmp(Node),
- Conf.
+stop_agent(Node) ->
+ stop_snmp(Node).
agent_load_mib(Node, Mib) ->
rcall(Node, snmpa, load_mibs, [[Mib]]).
@@ -6015,17 +6161,18 @@ stop_node(Node) ->
rpc:cast(Node, erlang, halt, []),
await_stopped(Node, 5).
-await_stopped(_, 0) ->
+await_stopped(Node, 0) ->
+ p("await_stopped -> ~p still exist: giving up", [Node]),
ok;
await_stopped(Node, N) ->
Nodes = erlang:nodes(),
case lists:member(Node, Nodes) of
true ->
- ?DBG("[~w] ~p still exist", [N, Node]),
+ p("await_stopped -> ~p still exist: ~w", [Node, N]),
?SLEEP(1000),
await_stopped(Node, N-1);
false ->
- ?DBG("[~w] ~p gone", [N, Node]),
+ p("await_stopped -> ~p gone: ~w", [Node, N]),
ok
end.
@@ -6271,7 +6418,7 @@ p(F, A) ->
p(TName, F, A) ->
io:format("*** [~w][~s] ***"
- "~n" ++ F ++ "~n", [TName, formated_timestamp()|A]).
+ "~n " ++ F ++ "~n", [TName, formated_timestamp()|A]).
formated_timestamp() ->
snmp_test_lib:formated_timestamp().
diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl
index 40fcbce8f1..d4eb00ff91 100644
--- a/lib/snmp/test/snmp_test_mgr.erl
+++ b/lib/snmp/test/snmp_test_mgr.erl
@@ -657,7 +657,8 @@ make_vb(Oid) ->
#varbind{oid = Oid, variabletype = 'NULL', value = 'NULL'}.
make_request_id() ->
- random:uniform(16#FFFFFFF-1).
+ %% random:uniform(16#FFFFFFF-1).
+ snmp_test_mgr_counter_server:increment(mgr_request_id, 1, 1, 2147483647).
echo_pdu(PDU, MiniMIB) ->
io:format("~s", [snmp_misc:format_pdu(PDU, MiniMIB)]).
diff --git a/lib/snmp/test/snmp_test_mgr_counter_server.erl b/lib/snmp/test/snmp_test_mgr_counter_server.erl
new file mode 100644
index 0000000000..db31e0380b
--- /dev/null
+++ b/lib/snmp/test/snmp_test_mgr_counter_server.erl
@@ -0,0 +1,152 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014-2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% The reason for this (test) counter server is that the
+%% agent test suite is implemented in such a way that the
+%% agent is started once and then used for several test cases.
+%% Each request is given a request id which *was* generated using
+%% random! It is therefor possible, although unlikely, that a
+%% request may get a request id that has recently been used,
+%% which will cause the agent to silently reject the request.
+%% For this reason, we start this server at the start of the
+%% agent suite and stop it at the end and all request ids are
+%% generated by this server.
+%%
+
+-module(snmp_test_mgr_counter_server).
+
+-export([start/0, stop/0, increment/4]).
+
+-define(SERVER, ?MODULE).
+-define(TAB, snmp_test_mgr_counter_tab).
+
+
+%%%-------------------------------------------------------------------
+%%% API
+%%%-------------------------------------------------------------------
+
+-spec start() -> ok.
+
+start() ->
+ Parent = self(),
+ ReqIdServer = spawn(fun() -> init(Parent) end),
+ receive
+ {ReqIdServer, ok} ->
+ ok;
+ {ReqIdServer, {error, Reason}} ->
+ exit({failed_starting_counter_server, Reason})
+ after 5000 ->
+ exit(ReqIdServer, kill), % Cleanup, just in case
+ exit({failed_starting_counter_server, timeout})
+ end.
+
+-spec stop() -> {ok, Counters :: list()} | {error, Reason :: term()}.
+
+stop() ->
+ request(stop).
+
+
+-spec increment(Counter :: atom(),
+ Initial :: non_neg_integer(),
+ Increment :: pos_integer(),
+ Max :: pos_integer()) ->
+ Next :: pos_integer().
+
+increment(Counter, Initial, Increment, Max) ->
+ Request = {increment, Counter, Initial, Increment, Max},
+ case request(Request) of
+ {ok, ReqId} ->
+ ReqId;
+ {error, Reason} ->
+ exit(Reason)
+ end.
+
+
+request(Request) ->
+ Id = make_ref(),
+ Msg = {self(), Id, Request},
+ try
+ begin
+ global:send(?SERVER, Msg),
+ receive
+ {reply, Id, Reply} ->
+ {ok, Reply}
+ end
+ end
+ catch
+ T:E ->
+ {error, {T, E}}
+ end.
+
+
+%%%-------------------------------------------------------------------
+%%% Internal functions
+%%%-------------------------------------------------------------------
+
+init(Parent) ->
+ p("starting"),
+ case global:register_name(?SERVER, self()) of
+ yes ->
+ p("name registration ok"),
+ Parent ! {self(), ok};
+ no ->
+ p("name registration failed"),
+ Parent ! {self(), registration_failed},
+ exit(registration_failed)
+ end,
+ ets:new(?TAB, [set, named_table, {keypos, 1}]),
+ loop().
+
+loop() ->
+ receive
+ {From, Id, {increment, Counter, Initial, Increment, Max}} ->
+ Position = 2,
+ Threshold = Max,
+ SetValue = Initial,
+ UpdateOp = {Position, Increment, Threshold, SetValue},
+ NextVal =
+ try ets:update_counter(?TAB, Counter, UpdateOp) of
+ Next when is_integer(Next) ->
+ p("increment ~w: (next) ~w", [Counter, Next]),
+ Next
+ catch
+ error:badarg ->
+ %% Oups, first time
+ p("increment ~w: (initial) ~w", [Counter, Initial]),
+ ets:insert(?TAB, {Counter, Initial}),
+ Initial
+ end,
+ From ! {reply, Id, NextVal},
+ loop();
+
+ {From, Id, stop} ->
+ p("stop"),
+ Counters = ets:tab2list(?TAB),
+ From ! {reply, Id, Counters},
+ exit(normal)
+ end.
+
+
+p(F) ->
+ p(F, []).
+
+p(F, A) ->
+ io:format("*** [~s] COUNTER-SERVER [~w] " ++ F ++ "~n",
+ [snmp_test_lib:formated_timestamp(), self() | A]).
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index 533e313bdb..04c3cc9392 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 4.25.0.1
+SNMP_VSN = 4.25.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 0d88cbda7a..84d5e5c86e 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2013</year>
+ <year>2004</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,6 +29,176 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 3.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed timeout bug in ssh:connect.</p>
+ <p>
+ Own Id: OTP-11908</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Option <c>max_sessions</c> added to
+ <c>ssh:daemon/{2,3}</c>. This option, if set, limits the
+ number of simultaneous connections accepted by the
+ daemon.</p>
+ <p>
+ Own Id: OTP-11885</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 3.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixes the problem that ssh_cli in some cases could delay
+ the prompt if a tty was not requested by the client.</p>
+ <p>
+ Own Id: OTP-10732</p>
+ </item>
+ <item>
+ <p>
+ The variable NewCol is now correctly calculated allowing
+ for tab-completion of function calls even when preceded
+ with blank space (Thanks to Alexander Demidenko)</p>
+ <p>
+ Own Id: OTP-11566</p>
+ </item>
+ <item>
+ <p>
+ Fix incorrect dialyzer spec and types, also enhance
+ documentation. </p>
+ <p>
+ Thanks to Ayaz Tuncer.</p>
+ <p>
+ Own Id: OTP-11627</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug when ssh:exec executes a linux command on a
+ linux ssh daemon. If the result is sent back from
+ standard error, the length information was not stripped
+ off correctly.</p>
+ <p>
+ Own Id: OTP-11667</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug with the ssh file 'known_hosts' which made
+ the file grow with many equal entries.</p>
+ <p>
+ Own Id: OTP-11671</p>
+ </item>
+ <item>
+ <p>
+ Some local implementations of removing the last element
+ from a list are replaced by <c>lists:droplast/1</c>. Note
+ that this requires at least <c>stdlib-2.0</c>, which is
+ the stdlib version delivered in OTP 17.0. (Thanks to Hans
+ Svensson)</p>
+ <p>
+ Own Id: OTP-11678</p>
+ </item>
+ <item>
+ <p>
+ Bug fix for <c>ssh:daemon/2,3</c> so that the failfun is
+ called when it should.</p>
+ <p>
+ Own Id: OTP-11680</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug which crashed ssh when SSH_MSG_KEX_DH_GEX_GROUP
+ is received. This could cause a vm-crash for eheap_alloc
+ during garbage collect.</p>
+ <p>
+ Own Id: OTP-11696 Aux Id: 12547, 12532 </p>
+ </item>
+ <item>
+ <p>
+ Fixes a bug that breaks keyboard-interactive
+ authentication. Thanks to Simon Cornish for reporting and
+ suggesting a fix.</p>
+ <p>
+ Own Id: OTP-11698</p>
+ </item>
+ <item>
+ <p>
+ dialyzer specs are now correct for <c>ssh:start/0</c>,
+ <c>ssh:start/1</c>, <c>ssh:stop/0</c> and
+ <c>ssh_connection_handler:open_channel/5</c>. (Thanks to
+ Johannes Weißl )</p>
+ <p>
+ Own Id: OTP-11705</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ <item>
+ <p>
+ Fixed dialyzer warning for <c>ssh_connection:send</c>.</p>
+ <p>
+ Own Id: OTP-11821</p>
+ </item>
+ <item>
+ <p>
+ <c>ssh:daemon/2,3</c> : Added options
+ <c>negotiation_timeout</c> and <c>parallel_login</c> to
+ tune the authentication behaviour.</p>
+ <p>
+ Own Id: OTP-11823</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Ssh now fully supports unicode filenames, filecontents,
+ shell and cli. Please note that the underlying os and
+ emulator must also give support for unicode. You may want
+ to start the emulator with "<c>erl +fnu</c>" on Linux.</p>
+ <p>
+ Own Id: OTP-10953</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 3.0</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 eaf96d0230..5a141ced3c 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -304,6 +304,36 @@
<c><![CDATA[true]]></c> if the password is valid and
<c><![CDATA[false]]></c> otherwise.</p>
</item>
+
+ <tag><c><![CDATA[{negotiation_timeout, integer()}]]></c></tag>
+ <item>
+ <p>Max time in milliseconds for the authentication negotiation. The default value is 2 minutes. If the client fails to login within this time, the connection is closed.
+ </p>
+ </item>
+
+ <tag><c><![CDATA[{max_sessions, pos_integer()}]]></c></tag>
+ <item>
+ <p>The maximum number of simultaneous sessions that are accepted at any time for this daemon. This includes sessions that are being authorized. So if set to <c>N</c>, and <c>N</c> clients have connected but not started the login process, the <c>N+1</c> connection attempt will be aborted. If <c>N</c> connections are authenticated and still logged in, no more loggins will be accepted until one of the existing ones log out.
+ </p>
+ <p>The counter is per listening port, so if two daemons are started, one with <c>{max_sessions,N}</c> and the other with <c>{max_sessions,M}</c> there will be in total <c>N+M</c> connections accepted for the whole ssh application.
+ </p>
+ <p>Note that if <c>parallel_login</c> is <c>false</c>, only one client at a time may be in the authentication phase.
+ </p>
+ <p>As default, the option is not set. This means that the number is not limited.
+ </p>
+ </item>
+
+ <tag><c><![CDATA[{parallel_login, boolean()}]]></c></tag>
+ <item>
+ <p>If set to false (the default value), only one login is handled a time. If set to true, an unlimited number of login attempts will be allowed simultanously.
+ </p>
+ <p>If the <c>max_sessions</c> option is set to <c>N</c> and <c>parallel_login</c> is set to <c>true</c>, the max number of simultaneous login attempts at any time is limited to <c>N-K</c> where <c>K</c> is the number of authenticated connections present at this daemon.
+ </p>
+ <warning>
+ <p>Do not enable <c>parallel_logins</c> without protecting the server by other means, for example the <c>max_sessions</c> option or a firewall configuration. If set to <c>true</c>, there is no protection against DOS attacks.</p>
+ </warning>
+ </item>
+
<tag><c><![CDATA[{key_cb, atom()}]]></c></tag>
<item>
<p>Module implementing the behaviour <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
diff --git a/lib/ssh/doc/src/using_ssh.xml b/lib/ssh/doc/src/using_ssh.xml
index 4d73366f5e..9ab71260d3 100644
--- a/lib/ssh/doc/src/using_ssh.xml
+++ b/lib/ssh/doc/src/using_ssh.xml
@@ -33,7 +33,7 @@
all needed applications (crypto, public_key and ssh). All examples
are run in an Erlang shell, or in a bash shell using openssh to
illustrate how the erlang ssh application can be used. The
- exampels are run as the user otptest on a local network where the
+ examples are run as the user otptest on a local network where the
user is authorized to login in over ssh to the host "tarlop". If
nothing else is stated it is persumed that the otptest user has an
entry in tarlop's authorized_keys file (may log in via ssh without
@@ -88,7 +88,7 @@
[...]
</code>
- <p>Create the file /tmp/otptest_user/.ssh/authrized_keys and add the content
+ <p>Create the file /tmp/otptest_user/.ssh/authorized_keys and add the content
of /tmp/otptest_user/.ssh/id_rsa.pub Now we can do</p>
<code type="erl">
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 74d7293be0..e0a51b3574 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -38,6 +38,8 @@
{registered, []},
{applications, [kernel, stdlib, crypto, public_key]},
{env, []},
- {mod, {ssh_app, []}}]}.
+ {mod, {ssh_app, []}},
+ {runtime_dependencies, ["stdlib-2.0","public_key-0.22","kernel-3.0",
+ "erts-6.0","crypto-3.3"]}]}.
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 1917c95f5a..42eb2167e0 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -19,9 +19,13 @@
{"%VSN%",
[
+ {"3.0.1", [{load_module, ssh, soft_purge, soft_purge, []},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, []}]},
{<<".*">>, [{restart_application, ssh}]}
],
[
+ {"3.0.1", [{load_module, ssh, soft_purge, soft_purge, []},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, []}]},
{<<".*">>, [{restart_application, ssh}]}
]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index d50d5a0cb3..240de69eff 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -1,7 +1,7 @@
-%%
+%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -73,8 +73,9 @@ connect(Host, Port, Options, Timeout) ->
{SocketOptions, SshOptions} ->
{_, Transport, _} = TransportOpts =
proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
+ ConnectionTimeout = proplists:get_value(connect_timeout, Options, infinity),
Inet = proplists:get_value(inet, SshOptions, inet),
- try Transport:connect(Host, Port, [ {active, false}, Inet | SocketOptions], Timeout) of
+ try Transport:connect(Host, Port, [ {active, false}, Inet | SocketOptions], ConnectionTimeout) of
{ok, Socket} ->
Opts = [{user_pid, self()}, {host, Host} | fix_idle_time(SshOptions)],
ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
@@ -332,6 +333,14 @@ handle_option([{idle_time, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{rekey_limit, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{max_sessions, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{negotiation_timeout, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{parallel_login, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([parallel_login|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option({parallel_login,true}) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
@@ -360,6 +369,12 @@ handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), leng
end;
handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value == infinity ->
Opt;
+handle_ssh_option({max_sessions, Value} = Opt) when is_integer(Value), Value>0 ->
+ Opt;
+handle_ssh_option({negotiation_timeout, Value} = Opt) when is_integer(Value); Value == infinity ->
+ Opt;
+handle_ssh_option({parallel_login, Value} = Opt) when Value==true ; Value==false ->
+ Opt;
handle_ssh_option({user, Value} = Opt) when is_list(Value) ->
Opt;
handle_ssh_option({dsa_pass_phrase, Value} = Opt) when is_list(Value) ->
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index 91905b2eaf..7302196674 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -80,15 +80,36 @@ acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout) ->
ListenSocket, AcceptTimeout)
end.
-handle_connection(_Callback, Address, Port, Options, Socket) ->
+handle_connection(Callback, Address, Port, Options, Socket) ->
SystemSup = ssh_system_sup:system_supervisor(Address, Port),
- {ok, SubSysSup} = ssh_system_sup:start_subsystem(SystemSup, Options),
- ConnectionSup = ssh_subsystem_sup:connection_supervisor(SubSysSup),
- ssh_connection_handler:start_connection(server, Socket,
- [{supervisors, [{system_sup, SystemSup},
- {subsystem_sup, SubSysSup},
- {connection_sup, ConnectionSup}]}
- | Options], infinity).
+ SSHopts = proplists:get_value(ssh_opts, Options, []),
+ MaxSessions = proplists:get_value(max_sessions,SSHopts,infinity),
+ case number_of_connections(SystemSup) < MaxSessions of
+ true ->
+ {ok, SubSysSup} = ssh_system_sup:start_subsystem(SystemSup, Options),
+ ConnectionSup = ssh_subsystem_sup:connection_supervisor(SubSysSup),
+ Timeout = proplists:get_value(negotiation_timeout, SSHopts, 2*60*1000),
+ ssh_connection_handler:start_connection(server, Socket,
+ [{supervisors, [{system_sup, SystemSup},
+ {subsystem_sup, SubSysSup},
+ {connection_sup, ConnectionSup}]}
+ | Options], Timeout);
+ false ->
+ Callback:close(Socket),
+ IPstr = if is_tuple(Address) -> inet:ntoa(Address);
+ true -> Address
+ end,
+ Str = try io_lib:format('~s:~p',[IPstr,Port])
+ catch _:_ -> "port "++integer_to_list(Port)
+ end,
+ error_logger:info_report("Ssh login attempt to "++Str++" denied due to option "
+ "max_sessions limits to "++ io_lib:write(MaxSessions) ++
+ " sessions."
+ ),
+ {error,max_sessions}
+ end.
+
+
handle_error(timeout) ->
ok;
@@ -114,3 +135,10 @@ handle_error(Reason) ->
String = lists:flatten(io_lib:format("Accept error: ~p", [Reason])),
error_logger:error_report(String),
exit({accept_failed, String}).
+
+
+number_of_connections(SystemSup) ->
+ length([X ||
+ {R,X,supervisor,[ssh_subsystem_sup]} <- supervisor:which_children(SystemSup),
+ is_reference(R)
+ ]).
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 409a1db6d5..45fd907383 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -196,7 +196,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
{authorized, User,
ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)};
false ->
- {not_authorized, {User, {passwd, Password}},
+ {not_authorized, {User, {error,"Bad user or password"}},
ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
authentications = "",
partial_success = false}, Ssh)}
@@ -228,7 +228,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
ssh_transport:ssh_packet(
#ssh_msg_userauth_success{}, Ssh)};
false ->
- {not_authorized, {User, {error, "Invalid signature"}},
+ {not_authorized, {User, undefined},
ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
authentications="publickey,password",
partial_success = false}, Ssh)}
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 070a2db5a8..06866392da 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -110,8 +110,16 @@ start_connection(server = Role, Socket, Options, Timeout) ->
{ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Opts]),
{_, Callback, _} = proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
socket_control(Socket, Pid, Callback),
- Ref = erlang:monitor(process, Pid),
- handshake(Pid, Ref, Timeout)
+ case proplists:get_value(parallel_login, Opts, false) of
+ true ->
+ spawn(fun() ->
+ Ref = erlang:monitor(process, Pid),
+ handshake(Pid, Ref, Timeout)
+ end);
+ false ->
+ Ref = erlang:monitor(process, Pid),
+ handshake(Pid, Ref, Timeout)
+ end
catch
exit:{noproc, _} ->
{error, ssh_not_started};
@@ -206,7 +214,7 @@ global_request(ConnectionHandler, Type, false = Reply, Data) ->
send_all_state_event(ConnectionHandler, {global_request, self(), Type, Reply, Data}).
%%--------------------------------------------------------------------
--spec send(pid(), channel_id(), integer(), iolist(), timeout()) ->
+-spec send(pid(), channel_id(), integer(), iodata(), timeout()) ->
ok | {error, timeout} | {error, closed}.
%%--------------------------------------------------------------------
send(ConnectionHandler, ChannelId, Type, Data, Timeout) ->
@@ -1474,8 +1482,7 @@ ssh_channel_info([ _ | Rest], Channel, Acc) ->
log_error(Reason) ->
Report = io_lib:format("Erlang ssh connection handler failed with reason: "
- "~p ~n, Stacktace: ~p ~n"
- "please report this to [email protected] \n",
+ "~p ~n, Stacktrace: ~p ~n",
[Reason, erlang:get_stacktrace()]),
error_logger:error_report(Report),
"Internal error".
diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl
index 832b144db9..35336bce8b 100644
--- a/lib/ssh/src/ssh_io.erl
+++ b/lib/ssh/src/ssh_io.erl
@@ -81,6 +81,8 @@ format(Fmt, Args) ->
trim(Line) when is_list(Line) ->
lists:reverse(trim1(lists:reverse(trim1(Line))));
+trim(Line) when is_binary(Line) ->
+ trim(unicode:characters_to_list(Line));
trim(Other) -> Other.
trim1([$\s|Cs]) -> trim(Cs);
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index d2e52379fa..ba38c1da40 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -47,21 +47,28 @@ all() ->
daemon_already_started,
server_password_option,
server_userpassword_option,
- double_close].
+ double_close,
+ ssh_connect_timeout,
+ ssh_connect_arg4_timeout,
+ {group, hardening_tests}
+ ].
groups() ->
[{dsa_key, [], basic_tests()},
{rsa_key, [], basic_tests()},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
- {internal_error, [], [internal_error]}
+ {internal_error, [], [internal_error]},
+ {hardening_tests, [], [max_sessions]}
].
+
basic_tests() ->
[send, close, peername_sockname,
exec, exec_compressed, shell, cli, known_hosts,
idle_time, rekey, openssh_zlib_basic_test].
+
%%--------------------------------------------------------------------
init_per_suite(Config) ->
case catch crypto:start() of
@@ -74,6 +81,8 @@ end_per_suite(_Config) ->
ssh:stop(),
crypto:stop().
%%--------------------------------------------------------------------
+init_per_group(hardening_tests, Config) ->
+ init_per_group(dsa_key, Config);
init_per_group(dsa_key, Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
@@ -103,6 +112,8 @@ init_per_group(internal_error, Config) ->
init_per_group(_, Config) ->
Config.
+end_per_group(hardening_tests, Config) ->
+ end_per_group(dsa_key, Config);
end_per_group(dsa_key, Config) ->
PrivDir = ?config(priv_dir, Config),
ssh_test_lib:clean_dsa(PrivDir),
@@ -620,6 +631,86 @@ double_close(Config) when is_list(Config) ->
ok = ssh:close(CM).
%%--------------------------------------------------------------------
+ssh_connect_timeout() ->
+ [{doc, "Test connect_timeout option in ssh:connect/4"}].
+ssh_connect_timeout(_Config) ->
+ ConnTimeout = 2000,
+ {error,{faked_transport,connect,TimeoutToTransport}} =
+ ssh:connect("localhost", 12345,
+ [{transport,{tcp,?MODULE,tcp_closed}},
+ {connect_timeout,ConnTimeout}],
+ 1000),
+ case TimeoutToTransport of
+ ConnTimeout -> ok;
+ Other ->
+ ct:log("connect_timeout is ~p but transport received ~p",[ConnTimeout,Other]),
+ {fail,"ssh:connect/4 wrong connect_timeout received in transport"}
+ end.
+
+%% Help for the test above
+connect(_Host, _Port, _Opts, Timeout) ->
+ {error, {faked_transport,connect,Timeout}}.
+
+
+%%--------------------------------------------------------------------
+ssh_connect_arg4_timeout() ->
+ [{doc, "Test fourth argument in ssh:connect/4"}].
+ssh_connect_arg4_timeout(_Config) ->
+ Timeout = 1000,
+ Parent = self(),
+ %% start the server
+ Server = spawn(fun() ->
+ {ok,Sl} = gen_tcp:listen(0,[]),
+ {ok,{_,Port}} = inet:sockname(Sl),
+ Parent ! {port,self(),Port},
+ Rsa = gen_tcp:accept(Sl),
+ ct:log("Server gen_tcp:accept got ~p",[Rsa]),
+ receive after 2*Timeout -> ok end %% let client timeout first
+ end),
+
+ %% Get listening port
+ Port = receive
+ {port,Server,ServerPort} -> ServerPort
+ end,
+
+ %% try to connect with a timeout, but "supervise" it
+ Client = spawn(fun() ->
+ T0 = now(),
+ Rc = ssh:connect("localhost",Port,[],Timeout),
+ ct:log("Client ssh:connect got ~p",[Rc]),
+ Parent ! {done,self(),Rc,T0}
+ end),
+
+ %% Wait for client reaction on the connection try:
+ receive
+ {done, Client, {error,_E}, T0} ->
+ Msp = ms_passed(T0, now()),
+ exit(Server,hasta_la_vista___baby),
+ Low = 0.9*Timeout,
+ High = 1.1*Timeout,
+ ct:log("Timeout limits: ~p--~p, timeout was ~p, expected ~p",[Low,High,Msp,Timeout]),
+ if
+ Low<Msp, Msp<High -> ok;
+ true -> {fail, "timeout not within limits"}
+ end;
+ {done, Client, {ok,_Ref}, _T0} ->
+ {fail,"ssh-connected ???"}
+ after
+ 5000 ->
+ exit(Server,hasta_la_vista___baby),
+ exit(Client,hasta_la_vista___baby),
+ {fail, "Didn't timeout"}
+ end.
+
+
+%% Help function
+%% N2-N1
+ms_passed(N1={_,_,M1}, N2={_,_,M2}) ->
+ {0,{0,Min,Sec}} = calendar:time_difference(calendar:now_to_local_time(N1),
+ calendar:now_to_local_time(N2)),
+ 1000 * (Min*60 + Sec + (M2-M1)/1000000).
+
+%%--------------------------------------------------------------------
openssh_zlib_basic_test() ->
[{doc, "Test basic connection with openssh_zlib"}].
@@ -639,6 +730,49 @@ openssh_zlib_basic_test(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+
+max_sessions(Config) ->
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+ MaxSessions = 2,
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {user_passwords, [{"carni", "meat"}]},
+ {parallel_login, true},
+ {max_sessions, MaxSessions}
+ ]),
+
+ Connect = fun() ->
+ R=ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user_interaction, false},
+ {user, "carni"},
+ {password, "meat"}
+ ]),
+ ct:log("Connection ~p up",[R])
+ end,
+
+ try [Connect() || _ <- lists:seq(1,MaxSessions)]
+ of
+ _ ->
+ ct:pal("Expect Info Report:",[]),
+ try Connect()
+ of
+ _ConnectionRef ->
+ ssh:stop_daemon(Pid),
+ {fail,"Too many connections accepted"}
+ catch
+ error:{badmatch,{error,"Connection closed"}} ->
+ ssh:stop_daemon(Pid),
+ ok
+ end
+ catch
+ error:{badmatch,{error,"Connection closed"}} ->
+ ssh:stop_daemon(Pid),
+ {fail,"Too few connections accepted"}
+ end.
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_unicode_SUITE.erl b/lib/ssh/test/ssh_unicode_SUITE.erl
index a896a425b9..cc916673b3 100644
--- a/lib/ssh/test/ssh_unicode_SUITE.erl
+++ b/lib/ssh/test/ssh_unicode_SUITE.erl
@@ -1,10 +1,7 @@
-%% Next line needed to enable utf8-strings in Erlang:
-%% -*- coding: utf-8 -*-
-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 9ffc59dbaf..40ed27d8f5 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 3.0.1
+SSH_VSN = 3.0.2
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 0b28b1ebd4..c61b2a9c2f 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -25,7 +25,164 @@
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the SSL application.</p>
- <section><title>SSL 5.3.3</title>
+ <section><title>SSL 5.3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix incorrect dialyzer spec and types, also enhance
+ documentation. </p>
+ <p>
+ Thanks to Ayaz Tuncer.</p>
+ <p>
+ Own Id: OTP-11627</p>
+ </item>
+ <item>
+ <p>
+ Fix possible mismatch between SSL/TLS version and default
+ ciphers. Could happen when you specified SSL/TLS-version
+ in optionlist to listen or accept.</p>
+ <p>
+ Own Id: OTP-11712</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Moved elliptic curve definition from the crypto
+ NIF/OpenSSL into Erlang code, adds the RFC-5639 brainpool
+ curves and makes TLS use them (RFC-7027).</p>
+ <p>
+ Thanks to Andreas Schultz</p>
+ <p>
+ Own Id: OTP-11578</p>
+ </item>
+ <item>
+ <p>
+ Unicode adaptations</p>
+ <p>
+ Own Id: OTP-11620</p>
+ </item>
+ <item>
+ <p>
+ Added option honor_cipher_order. This instructs the
+ server to prefer its own cipher ordering rather than the
+ client's and can help protect against things like BEAST
+ while maintaining compatability with clients which only
+ support older ciphers. </p>
+ <p>
+ Thanks to Andrew Thompson for the implementation, and
+ Andreas Schultz for the test cases.</p>
+ <p>
+ Own Id: OTP-11621</p>
+ </item>
+ <item>
+ <p>
+ Replace boolean checking in validate_option with
+ is_boolean guard. </p>
+ <p>
+ Thanks to Andreas Schultz.</p>
+ <p>
+ Own Id: OTP-11634</p>
+ </item>
+ <item>
+ <p>
+ Some function specs are corrected or moved and some edoc
+ comments are corrected in order to allow use of edoc.
+ (Thanks to Pierre Fenoll)</p>
+ <p>
+ Own Id: OTP-11702</p>
+ </item>
+ <item>
+ <p>
+ Correct clean up of certificate database when certs are
+ inputed in pure DER format.The incorrect code could cause
+ a memory leek when certs where inputed in DER. Thanks to
+ Bernard Duggan for reporting this.</p>
+ <p>
+ Own Id: OTP-11733</p>
+ </item>
+ <item>
+ <p>
+ Improved documentation of the cacertfile option</p>
+ <p>
+ Own Id: OTP-11759 Aux Id: seq12535 </p>
+ </item>
+ <item>
+ <p>
+ Avoid next protocol negotiation failure due to incorrect
+ option format.</p>
+ <p>
+ Own Id: OTP-11760</p>
+ </item>
+ <item>
+ <p>
+ Handle v1 CRLs, with no extensions and fixes issues with
+ IDP (Issuing Distribution Point) comparison during CRL
+ validation. </p>
+ <p>
+ Thanks to Andrew Thompson</p>
+ <p>
+ Own Id: OTP-11761</p>
+ </item>
+ <item>
+ <p>
+ Server now ignores client ECC curves that it does not
+ support instead of crashing. </p>
+ <p>
+ Thanks to Danil Zagoskin for reporting the issue and
+ suggesting a solution.</p>
+ <p>
+ Own Id: OTP-11780</p>
+ </item>
+ <item>
+ <p>
+ Handle SNI (Server Name Indication) alert
+ unrecognized_name and gracefully deal with unexpected
+ alerts. </p>
+ <p>
+ Thanks to Masatake Daimon for reporting this.</p>
+ <p>
+ Own Id: OTP-11815</p>
+ </item>
+ <item>
+ <p>
+ Add possibility to specify ssl options when calling
+ ssl:ssl_accept</p>
+ <p>
+ Own Id: OTP-11837</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 5.3.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 910dca3889..ffee4bd1af 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -184,12 +184,6 @@
<item> The DER encoded trusted certificates. If this option
is supplied it will override the cacertfile option.</item>
- <tag>{cacertfile, path()}</tag>
- <item>Path to file containing PEM encoded
- CA certificates (trusted certificates used for verifying a peer
- certificate). May be omitted if you do not want to verify
- the peer.</item>
-
<tag>{ciphers, ciphers()}</tag>
<item>The cipher suites that should be supported. The function
<c>cipher_suites/0</c> can be used to find all ciphers that are
@@ -354,7 +348,13 @@ fun(srp, Username :: string(), UserState :: term()) ->
<item>Specifies if client should try to reuse sessions
when possible.
</item>
-
+
+ <tag>{cacertfile, path()}</tag>
+ <item>The path to a file containing PEM encoded CA certificates. The CA
+ certificates are used during server authentication and when building the
+ client certificate chain.
+ </item>
+
<tag>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()]}}</tag>
<tag>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()], Default :: binary()}}</tag>
<item>
@@ -403,7 +403,17 @@ fun(srp, Username :: string(), UserState :: term()) ->
meaning in the server than in the client.</p>
<taglist>
-
+
+ <tag>{cacertfile, path()}</tag>
+ <item>The path to a file containing PEM encoded CA
+ certificates. The CA certificates are used to build the server
+ certificate chain, and for client authentication. Also the CAs
+ are used in the list of acceptable client CAs passed to the
+ client when a certificate is requested. May be omitted if there
+ is no need to verify the client and if there are not any
+ intermediate CAs for the server certificate.
+ </item>
+
<tag>{dh, der_encoded()}</tag>
<item>The DER encoded Diffie Hellman parameters. If this option
is supplied it will override the dhfile option.
@@ -758,39 +768,45 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name>ssl_accept(ListenSocket) -> </name>
- <name>ssl_accept(ListenSocket, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Perform server-side SSL handshake</fsummary>
+ <name>ssl_accept(Socket) -> </name>
+ <name>ssl_accept(Socket, Timeout) -> ok | {error, Reason}</name>
+ <fsummary>Perform server-side SSL/TLS handshake</fsummary>
<type>
- <v>ListenSocket = sslsocket()</v>
+ <v>Socket = sslsocket()</v>
<v>Timeout = integer()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>The <c>ssl_accept</c> function establish the SSL connection
- on the server side. It should be called directly after
- <c>transport_accept</c>, in the spawned server-loop.</p>
+ <p> Performs the SSL/TLS server-side handshake <c>Socket</c> is a socket as returned
+ by <seealso
+ marker="#transport_accept-2">ssl:transport_accept/[1,2]</seealso>
+ </p>
</desc>
</func>
<func>
- <name>ssl_accept(ListenSocket, SslOptions) -> </name>
- <name>ssl_accept(ListenSocket, SslOptions, Timeout) -> {ok, Socket} | {error, Reason}</name>
- <fsummary>Perform server-side SSL handshake</fsummary>
+ <name>ssl_accept(Socket, SslOptions) -> </name>
+ <name>ssl_accept(Socket, SslOptions, Timeout) -> {ok, Socket} | ok | {error, Reason}</name>
+ <fsummary>Perform server-side SSL/TLS handshake</fsummary>
<type>
- <v>ListenSocket = socket()</v>
+ <v>Socket = socket() | sslsocket() </v>
<v>SslOptions = ssloptions()</v>
<v>Timeout = integer()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p> Upgrades a gen_tcp, or
- equivalent, socket to an ssl socket i.e. performs the
- ssl server-side handshake.</p>
+ <p> If <c>Socket</c> is a socket() - upgrades a gen_tcp, or equivalent, socket to an ssl socket
+ i.e. performs the SSL/TLS server-side handshake and returns the ssl socket.
+ </p>
+
<warning><p>Note that the listen socket should be in {active, false} mode
before telling the client that the server is ready to upgrade
- and calling this function, otherwise the upgrade may
+ by calling this function, otherwise the upgrade may
or may not succeed depending on timing.</p></warning>
+
+ <p> If <c>Socket</c> is an sslsocket() - provides additional SSL/TLS options to those specified in <seealso
+ marker="#listen-2">ssl:listen/2 </seealso> and then performs the SSL/TLS handshake.
+ </p>
</desc>
</func>
@@ -832,33 +848,38 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name>transport_accept(Socket) -></name>
- <name>transport_accept(Socket, Timeout) ->
+ <name>transport_accept(ListenSocket) -></name>
+ <name>transport_accept(ListenSocket, Timeout) ->
{ok, NewSocket} | {error, Reason}</name>
<fsummary>Accept an incoming connection and
prepare for <c>ssl_accept</c></fsummary>
<type>
- <v>Socket = NewSocket = sslsocket()</v>
+ <v>ListenSocket = NewSocket = sslsocket()</v>
<v>Timeout = integer()</v>
<v>Reason = reason()</v>
</type>
<desc>
<p>Accepts an incoming connection request on a listen socket.
- <c>ListenSocket</c> must be a socket returned from
- <c>listen/2</c>. The socket returned should be passed to
- <c>ssl_accept</c> to complete ssl handshaking and
- establishing the connection.</p>
+ <c>ListenSocket</c> must be a socket returned from
+ <seealso
+ marker="#listen-2"> ssl:listen/2</seealso>.
+ The socket returned should be passed to
+ <seealso marker="#ssl_accept-2"> ssl:ssl_accept[2,3]</seealso>
+ to complete handshaking i.e
+ establishing the SSL/TLS connection.</p>
<warning>
- <p>The socket returned can only be used with <c>ssl_accept</c>,
- no traffic can be sent or received before that call.</p>
+ <p>The socket returned can only be used with
+ <seealso marker="#ssl_accept-2"> ssl:ssl_accept[2,3]</seealso>
+ no traffic can be sent or received before that call.</p>
</warning>
<p>The accepted socket inherits the options set for
- <c>ListenSocket</c> in <c>listen/2</c>.</p>
+ <c>ListenSocket</c> in <seealso
+ marker="#listen-2"> ssl:listen/2</seealso>.</p>
<p>The default
- value for <c>Timeout</c> is <c>infinity</c>. If
- <c>Timeout</c> is specified, and no connection is accepted
- within the given time, <c>{error, timeout}</c> is
- returned.</p>
+ value for <c>Timeout</c> is <c>infinity</c>. If
+ <c>Timeout</c> is specified, and no connection is accepted
+ within the given time, <c>{error, timeout}</c> is
+ returned.</p>
</desc>
</func>
diff --git a/lib/ssl/internal_doc/ssl-implementation.txt b/lib/ssl/internal_doc/ssl-implementation.txt
deleted file mode 100644
index e5d6ac8cd0..0000000000
--- a/lib/ssl/internal_doc/ssl-implementation.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-
-Important modules:
-
- module behaviour children
- ------ ---------
- ssl_app application ssl_sup
- ssl_sup supervisor ssl_server, ssl_broker_sup
- ssl_server gen_server -
- ssl_broker_sup supervisor ssl_broker
- ssl_broker gen_server -
-
-The ssl_server controls a port program that implements the SSL functionality.
-That port program uses the OpenSSL package.
-
-Each socket has a corresponding broker (listen, accept or connect). A broker
-is created and supervised by the ssl_broker_sup.
-
-All communication is between a user and a broker. The broker communicates
-with the ssl_server, that sends its commands to the port program and handles
-the port program responses, that are distributed to users through the
-brokers.
-
-There is a distinction between commands and data flow between the ssl_server
-and the port program. Each established connection between the user and the
-outside world consists of a local erlang socket (owned by the broker) that
-is read from and written to by the broker. At the other end of the local
-connection is a local socket in the port program.
-
-The "real" socket that connects to the outside world is in the port program
-(including listen sockets). The main purpose of the port program is to
-shuffle data between local sockets and outside world sockets, and detect and
-propagate read and write errors (including detection of closed sockets) to
-the ssl_server.
-
-There is documentation in the ssl_broker.erl module.
-
-There is also documentation in the esock.c and esock_openssl.c files.
-
-The ssl_pem.erl, ssl_pkix.erl and ssl_base64.erl modules are support
-modules for reading SSL certificates. Modules for parsing certificates
-are generated from ASN.1 modules in the `pkix' directory.
-
-The `examples' directory contains functions for generating certificates.
-Those certificates are used in the test suites.
-
-
-
-
-
-
-
-
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index 131b615277..7c4c8ec2cc 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2013. All Rights Reserved.
+# Copyright Ericsson AB 1999-2014. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -66,6 +66,7 @@ MODULES= \
ssl_session \
ssl_session_cache \
ssl_socket \
+ ssl_listen_tracker_sup \
tls_record \
dtls_record \
ssl_record \
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index da2e076856..508983ddac 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,222 +20,515 @@
%% Internal application API
-%%====================================================================
+-behaviour(gen_fsm).
+
+-include("dtls_connection.hrl").
+-include("dtls_handshake.hrl").
+-include("ssl_alert.hrl").
+-include("dtls_record.hrl").
+-include("ssl_cipher.hrl").
+-include("ssl_api.hrl").
+-include("ssl_internal.hrl").
+-include("ssl_srp.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
%% Internal application API
-%%====================================================================
+%% Setup
+-export([start_fsm/8]).
+
+%% State transition handling
+-export([next_record/1, next_state/4%,
+ %%next_state_connection/2
+ ]).
+
+%% Handshake handling
+-export([%%renegotiate/1,
+ send_handshake/2, send_change_cipher/2]).
+
+%% Alert and close handling
+-export([send_alert/2, handle_own_alert/4, %%handle_close_alert/3,
+ handle_normal_shutdown/3
+ %%handle_unexpected_message/3,
+ %%alert_user/5, alert_user/8
+ ]).
+
+%% Data handling
+-export([%%write_application_data/3,
+ read_application_data/2%%,
+%% passive_receive/2, next_record_if_active/1
+ ]).
+
+%% Called by tls_connection_sup
+-export([start_link/7]).
+%% gen_fsm callbacks
+-export([init/1, hello/2, certify/2, cipher/2,
+ abbreviated/2, connection/2, handle_event/3,
+ handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
-%% %%====================================================================
-%% %% State functions
-%% %%====================================================================
-
-%% -spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(),
-%% #state{}) -> gen_fsm_state_return().
-%% %%--------------------------------------------------------------------
-%% hello(start, #state{host = Host, port = Port, role = client,
-%% ssl_options = SslOpts,
-%% session = #session{own_certificate = Cert} = Session0,
-%% session_cache = Cache, session_cache_cb = CacheCb,
-%% connection_states = ConnectionStates0,
-%% renegotiation = {Renegotiation, _},
-%% client_cookie = Cookie} = State0) ->
-%% Hello = dtls_handshake:client_hello(Host, Port, Cookie, ConnectionStates0, SslOpts,
-%% Cache, CacheCb, Renegotiation, Cert),
-
-%% Version = Hello#client_hello.client_version,
-%% State1 = State0#state{negotiated_version = Version, %% Requested version
-%% session =
-%% Session0#session{session_id = Hello#client_hello.session_id},
-%% dtls_handshake_history = ssl_handshake:init_handshake_history()},
-
-%% State2 = send_flight(Hello, waiting, State1),
-
-%% {Record, State} = next_record(State2),
-%% next_state(hello, hello, Record, State);
-
-%% hello(start, #state{role = server} = State0) ->
-%% {Record, State} = next_record(State0),
-%% next_state(hello, hello, Record, State);
-
-%% hello(#hello_request{}, #state{role = client} = State0) ->
-%% {Record, State} = next_record(State0),
-%% next_state(hello, hello, Record, State);
-
-%% hello(#server_hello{cipher_suite = CipherSuite,
-%% compression_method = Compression} = Hello,
-%% #state{session = #session{session_id = OldId},
-%% connection_states = ConnectionStates0,
-%% role = client,
-%% negotiated_version = ReqVersion,
-%% renegotiation = {Renegotiation, _},
-%% ssl_options = SslOptions} = State1) ->
-%% State0 = flight_done(State1),
-%% case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
-%% #alert{} = Alert ->
-%% handle_own_alert(Alert, ReqVersion, hello, State0);
-%% {Version, NewId, ConnectionStates, NextProtocol} ->
-%% {KeyAlgorithm, _, _, _} =
-%% ssl_cipher:suite_definition(CipherSuite),
-
-%% PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
-
-%% NewNextProtocol = case NextProtocol of
-%% undefined ->
-%% State0#state.next_protocol;
-%% _ ->
-%% NextProtocol
-%% end,
-
-%% State = State0#state{key_algorithm = KeyAlgorithm,
-%% hashsign_algorithm = default_hashsign(Version, KeyAlgorithm),
-%% negotiated_version = Version,
-%% connection_states = ConnectionStates,
-%% premaster_secret = PremasterSecret,
-%% expecting_next_protocol_negotiation = NextProtocol =/= undefined,
-%% next_protocol = NewNextProtocol},
-
-%% case ssl_session:is_new(OldId, NewId) of
-%% true ->
-%% handle_new_session(NewId, CipherSuite, Compression,
-%% State#state{connection_states = ConnectionStates});
-%% false ->
-%% handle_resumed_session(NewId, State#state{connection_states = ConnectionStates})
-%% end
-%% end;
-
-%% hello(#hello_verify_request{cookie = Cookie},
-%% #state{host = Host, port = Port,
-%% session = #session{own_certificate = Cert},
-%% session_cache = Cache, session_cache_cb = CacheCb,
-%% ssl_options = SslOpts,
-%% connection_states = ConnectionStates0,
-%% renegotiation = {Renegotiation, _}} = State0) ->
-%% Hello = ssl_handshake:client_hello(Host, Port, Cookie, ConnectionStates0, SslOpts,
-%% Cache, CacheCb, Renegotiation, Cert),
-%% State1 = State0#state{
-%% tls_handshake_history = ssl_handshake:init_handshake_history(),
-%% client_cookie = Cookie},
-%% State2 = send_flight(Hello, waiting, State1),
-
-%% {Record, State} = next_record(State2),
-%% next_state(hello, hello, Record, State);
-
-
-%% %%--------------------------------------------------------------------
-%% -spec abbreviated(#hello_request{} | #finished{} | term(),
-%% #state{}) -> gen_fsm_state_return().
-%% %%--------------------------------------------------------------------
-
-%% abbreviated(timeout, State) ->
-%% { next_state, abbreviated, State, hibernate };
-
-%% abbreviated(Msg, State) ->
-%% handle_unexpected_message(Msg, abbreviated, State).
-
-%% %%--------------------------------------------------------------------
-%% -spec certify(#hello_request{} | #certificate{} | #server_key_exchange{} |
-%% #certificate_request{} | #server_hello_done{} | #client_key_exchange{} | term(),
-%% #state{}) -> gen_fsm_state_return().
-%% %%--------------------------------------------------------------------
-
-
-%% certify(timeout, State) ->
-%% { next_state, certify, State, hibernate };
-
-%% certify(Msg, State) ->
-%% handle_unexpected_message(Msg, certify, State).
-
-
-%% %%--------------------------------------------------------------------
-%% -spec cipher(#hello_request{} | #certificate_verify{} | #finished{} | term(),
-%% #state{}) -> gen_fsm_state_return().
-%% %%--------------------------------------------------------------------
-
-%% cipher(timeout, State) ->
-%% { next_state, cipher, State, hibernate };
-
-%% cipher(Msg, State) ->
-%% handle_unexpected_message(Msg, cipher, State).
-
-%% %%--------------------------------------------------------------------
-%% -spec connection(#hello_request{} | #client_hello{} | term(),
-%% #state{}) -> gen_fsm_state_return().
-%% %%--------------------------------------------------------------------
-
-%% connection(timeout, State) ->
-%% {next_state, connection, State, hibernate};
-
-%% connection(Msg, State) ->
-%% handle_unexpected_message(Msg, connection, State).
-
-%% %%--------------------------------------------------------------------
-%% %%% Internal functions
-%% %%--------------------------------------------------------------------
-%% handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) ->
-%% Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
-%% handle_own_alert(Alert, Version, {Info, Msg}, State).
-
-%% send_flight(HandshakeRec, FlightState, State) ->
-%% send_flight(FlightState, buffer_flight(HandshakeRec, State)).
-
-%% send_flight(FlightState, State = #state{negotiated_version = Version,
-%% flight_buffer = Buffer}) ->
-
-%% State1 = do_send_flight(queue:to_list(Buffer), [], State),
-%% finish_send_flight(Version, FlightState, State1).
-
-%% resend_flight(State = #state{negotiated_version = Version,
-%% flight_state = FlightState,
-%% flight_buffer = Buffer})
-%% when FlightState == finished; FlightState == waiting ->
-%% State1 = do_send_flight(queue:to_list(Buffer), [], State),
-%% finish_send_flight(Version, FlightState, State1);
-
-%% resend_flight(State) ->
-%% State.
-
-%% flight_done(State) ->
-%% cancel_dtls_retransmit_timer(State#state{flight_state = done,
-%% flight_buffer = undefined}).
-
-%% do_send_flight([], BinMsgs, State = #state{transport_cb = Transport, socket = Socket}) ->
-%% Transport:send(Socket, lists:reverse(BinMsgs)),
-%% State;
-%% do_send_flight([{Epoch, MsgSeq, HandshakeRec}|T], BinMsgs0,
-%% State = #state{negotiated_version = Version,
-%% connection_states = ConnectionStates0}) ->
-%% CS0 = ssl_record:connection_state_by_epoch(ConnectionStates0, Epoch, write),
-%% {BinMsgs, CS1} = encode_handshake_rec(HandshakeRec, Version, MsgSeq, BinMsgs0, CS0),
-%% ConnectionStates1 = ssl_record:set_connection_state_by_epoch(ConnectionStates0, CS1, write),
-%% do_send_flight(T, BinMsgs, State#state{connection_states = ConnectionStates1}).
-
-%% cancel_dtls_retransmit_timer(State = #state{dtls_retransmit_timer = TimerRef}) ->
-%% cancel_timer(TimerRef),
-%% State#state{dtls_retransmit_timer = undefined}.
-
-%% rearm_dtls_retransmit_timer(State = #state{dtls_retransmit_timer = undefined}) ->
-%% TimerRef = erlang:start_timer(1000, self(), dtls_retransmit),
-%% State#state{dtls_retransmit_timer = TimerRef};
-%% rearm_dtls_retransmit_timer(State) ->
-%% State.
-
-%% finish_send_flight({254, _}, waiting, State) ->
-%% TimerRef = erlang:start_timer(1000, self(), dtls_retransmit),
-%% State#state{
-%% dtls_retransmit_timer = TimerRef,
-%% last_retransmit = timestamp(),
-%% flight_state = waiting};
-
-%% finish_send_flight(_, FlightState, State) ->
-%% State#state{flight_state = FlightState}.
-
-%% timestamp() ->
-%% {Mega, Sec, Micro} = erlang:now(),
-%% Mega * 1000000 * 1000 + Sec * 1000 + (Micro div 1000).
-
-%% encode_handshake_rec(HandshakeRec, Version, MsgSeq, BinMsgs0, CS0) ->
-%% {_, Fragments} = ssl_handshake:encode_handshake(HandshakeRec, Version, MsgSeq, 1400),
-%% lists:foldl(fun(F, {Bin, C0}) ->
-%% {B, C1} = ssl_record:encode_handshake(F, Version, C0),
-%% {[B|Bin], C1} end, {BinMsgs0, CS0}, Fragments).
+%%====================================================================
+%% Internal application API
+%%====================================================================
+start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_} = Opts,
+ User, {CbModule, _,_, _} = CbInfo,
+ Timeout) ->
+ try
+ {ok, Pid} = dtls_connection_sup:start_child([Role, Host, Port, Socket,
+ Opts, User, CbInfo]),
+ {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule),
+ ok = ssl_connection:handshake(SslSocket, Timeout),
+ {ok, SslSocket}
+ catch
+ error:{badmatch, {error, _} = Error} ->
+ Error
+ end;
+
+start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_} = Opts,
+ User, {CbModule, _,_, _} = CbInfo,
+ Timeout) ->
+ try
+ {ok, Pid} = dtls_connection_sup:start_child_dist([Role, Host, Port, Socket,
+ Opts, User, CbInfo]),
+ {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule),
+ ok = ssl_connection:handshake(SslSocket, Timeout),
+ {ok, SslSocket}
+ catch
+ error:{badmatch, {error, _} = Error} ->
+ Error
+ end.
+
+send_handshake(Handshake, #state{negotiated_version = Version,
+ tls_handshake_history = Hist0,
+ connection_states = ConnectionStates0} = State0) ->
+ {BinHandshake, ConnectionStates, Hist} =
+ encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
+ send_flight(BinHandshake, State0#state{connection_states = ConnectionStates,
+ tls_handshake_history = Hist
+ }).
+
+send_alert(Alert, #state{negotiated_version = Version,
+ socket = Socket,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0} = State0) ->
+ {BinMsg, ConnectionStates} =
+ ssl_alert:encode(Alert, Version, ConnectionStates0),
+ Transport:send(Socket, BinMsg),
+ State0#state{connection_states = ConnectionStates}.
+
+send_change_cipher(Msg, #state{connection_states = ConnectionStates0,
+ socket = Socket,
+ negotiated_version = Version,
+ transport_cb = Transport} = State0) ->
+ {BinChangeCipher, ConnectionStates} =
+ encode_change_cipher(Msg, Version, ConnectionStates0),
+ Transport:send(Socket, BinChangeCipher),
+ State0#state{connection_states = ConnectionStates}.
+
+%%====================================================================
+%% tls_connection_sup API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec start_link(atom(), host(), inet:port_number(), port(), list(), pid(), tuple()) ->
+ {ok, pid()} | ignore | {error, reason()}.
+%%
+%% Description: Creates a gen_fsm process which calls Module:init/1 to
+%% initialize. To ensure a synchronized start-up procedure, this function
+%% does not return until Module:init/1 has returned.
+%%--------------------------------------------------------------------
+start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
+ {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}.
+
+init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
+ process_flag(trap_exit, true),
+ State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
+ Handshake = ssl_handshake:init_handshake_history(),
+ TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ try ssl_config:init(SSLOpts0, Role) of
+ {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} ->
+ Session = State0#state.session,
+ State = State0#state{
+ tls_handshake_history = Handshake,
+ session = Session#session{own_certificate = OwnCert,
+ time_stamp = TimeStamp},
+ file_ref_db = FileRefHandle,
+ cert_db_ref = Ref,
+ cert_db = CertDbHandle,
+ session_cache = CacheHandle,
+ private_key = Key,
+ diffie_hellman_params = DHParams},
+ gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State))
+ catch
+ throw:Error ->
+ gen_fsm:enter_loop(?MODULE, [], error, {Error,State0}, get_timeout(State0))
+ end.
+
+%%--------------------------------------------------------------------
+%% Description:There should be one instance of this function for each
+%% possible state name. Whenever a gen_fsm receives an event sent
+%% using gen_fsm:send_event/2, the instance of this function with the
+%% same name as the current state name StateName is called to handle
+%% the event. It is also called if a timeout occurs.
+%%
+hello(start, #state{host = Host, port = Port, role = client,
+ ssl_options = SslOpts,
+ session = #session{own_certificate = Cert} = Session0,
+ session_cache = Cache, session_cache_cb = CacheCb,
+ transport_cb = Transport, socket = Socket,
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _}} = State0) ->
+ Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
+ Cache, CacheCb, Renegotiation, Cert),
+
+ Version = Hello#client_hello.client_version,
+ Handshake0 = ssl_handshake:init_handshake_history(),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Hello, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ State1 = State0#state{connection_states = ConnectionStates,
+ negotiated_version = Version, %% Requested version
+ session =
+ Session0#session{session_id = Hello#client_hello.session_id},
+ tls_handshake_history = Handshake},
+ {Record, State} = next_record(State1),
+ next_state(hello, hello, Record, State);
+
+hello(Hello = #client_hello{client_version = ClientVersion,
+ extensions = #hello_extensions{hash_signs = HashSigns}},
+ State = #state{connection_states = ConnectionStates0,
+ port = Port, session = #session{own_certificate = Cert} = Session0,
+ renegotiation = {Renegotiation, _},
+ session_cache = Cache,
+ session_cache_cb = CacheCb,
+ ssl_options = SslOpts}) ->
+ case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
+ ConnectionStates0, Cert}, Renegotiation) of
+ {Version, {Type, Session},
+ ConnectionStates,
+ #hello_extensions{ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves} = ServerHelloExt} ->
+ HashSign = ssl_handshake:select_hashsign(HashSigns, Cert,
+ dtls_v1:corresponding_tls_version(Version)),
+ ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign},
+ State#state{connection_states = ConnectionStates,
+ negotiated_version = Version,
+ session = Session,
+ client_ecc = {EllipticCurves, EcPointFormats}}, ?MODULE);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ClientVersion, hello, State)
+ end;
+hello(Hello,
+ #state{connection_states = ConnectionStates0,
+ negotiated_version = ReqVersion,
+ role = client,
+ renegotiation = {Renegotiation, _},
+ ssl_options = SslOptions} = State) ->
+ case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ReqVersion, hello, State);
+ {Version, NewId, ConnectionStates, NextProtocol} ->
+ ssl_connection:handle_session(Hello,
+ Version, NewId, ConnectionStates, NextProtocol, State)
+ end;
+
+hello(Msg, State) ->
+ ssl_connection:hello(Msg, State, ?MODULE).
+
+abbreviated(Msg, State) ->
+ ssl_connection:abbreviated(Msg, State, ?MODULE).
+
+certify(Msg, State) ->
+ ssl_connection:certify(Msg, State, ?MODULE).
+
+cipher(Msg, State) ->
+ ssl_connection:cipher(Msg, State, ?MODULE).
+
+connection(#hello_request{}, #state{host = Host, port = Port,
+ session = #session{own_certificate = Cert} = Session0,
+ session_cache = Cache, session_cache_cb = CacheCb,
+ ssl_options = SslOpts,
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _}} = State0) ->
+ Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
+ Cache, CacheCb, Renegotiation, Cert),
+ %% TODO DTLS version State1 = send_handshake(Hello, State0),
+ State1 = State0,
+ {Record, State} =
+ next_record(
+ State1#state{session = Session0#session{session_id
+ = Hello#client_hello.session_id}}),
+ next_state(connection, hello, Record, State);
+
+connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) ->
+ %% Mitigate Computational DoS attack
+ %% http://www.educatedguesswork.org/2011/10/ssltls_and_computational_dos.html
+ %% http://www.thc.org/thc-ssl-dos/ Rather than disabling client
+ %% initiated renegotiation we will disallow many client initiated
+ %% renegotiations immediately after each other.
+ erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate),
+ hello(Hello, State#state{allow_renegotiate = false});
+
+connection(#client_hello{}, #state{role = server, allow_renegotiate = false} = State0) ->
+ Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION),
+ State = send_alert(Alert, State0),
+ next_state_connection(connection, State);
+
+connection(Msg, State) ->
+ ssl_connection:connection(Msg, State, tls_connection).
+
+%%--------------------------------------------------------------------
+%% Description: Whenever a gen_fsm receives an event sent using
+%% gen_fsm:send_all_state_event/2, this function is called to handle
+%% the event. Not currently used!
+%%--------------------------------------------------------------------
+handle_event(_Event, StateName, State) ->
+ {next_state, StateName, State, get_timeout(State)}.
+
+%%--------------------------------------------------------------------
+%% Description: Whenever a gen_fsm receives an event sent using
+%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle
+%% the event.
+%%--------------------------------------------------------------------
+handle_sync_event(Event, From, StateName, State) ->
+ ssl_connection:handle_sync_event(Event, From, StateName, State).
+
+%%--------------------------------------------------------------------
+%% Description: This function is called by a gen_fsm when it receives any
+%% other message than a synchronous or asynchronous event
+%% (or a system message).
+%%--------------------------------------------------------------------
+
+%% raw data from socket, unpack records
+handle_info({Protocol, _, Data}, StateName,
+ #state{data_tag = Protocol} = State0) ->
+ %% Simplify for now to avoid dialzer warnings before implementation is compleate
+ %% case next_tls_record(Data, State0) of
+ %% {Record, State} ->
+ %% next_state(StateName, StateName, Record, State);
+ %% #alert{} = Alert ->
+ %% handle_normal_shutdown(Alert, StateName, State0),
+ %% {stop, {shutdown, own_alert}, State0}
+ %% end;
+ {Record, State} = next_tls_record(Data, State0),
+ next_state(StateName, StateName, Record, State);
+
+handle_info({CloseTag, Socket}, StateName,
+ #state{socket = Socket, close_tag = CloseTag,
+ negotiated_version = _Version} = State) ->
+ handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
+ {stop, {shutdown, transport_closed}, State};
+
+handle_info(Msg, StateName, State) ->
+ ssl_connection:handle_info(Msg, StateName, State).
+
+%%--------------------------------------------------------------------
+%% Description:This function is called by a gen_fsm when it is about
+%% to terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_fsm terminates with
+%% Reason. The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(Reason, StateName, State) ->
+ ssl_connection:terminate(Reason, StateName, State).
+
+%%--------------------------------------------------------------------
+%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, StateName, State, _Extra) ->
+ {ok, StateName, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+encode_handshake(Handshake, Version, ConnectionStates0, Hist0) ->
+ Seq = sequence(ConnectionStates0),
+ {EncHandshake, FragmentedHandshake} = dtls_handshake:encode_handshake(Handshake, Version,
+ Seq),
+ Hist = ssl_handshake:update_handshake_history(Hist0, EncHandshake),
+ {Encoded, ConnectionStates} =
+ dtls_record:encode_handshake(FragmentedHandshake,
+ Version, ConnectionStates0),
+ {Encoded, ConnectionStates, Hist}.
+
+next_record(#state{%%flight = #flight{state = finished},
+ protocol_buffers =
+ #protocol_buffers{dtls_packets = [], dtls_cipher_texts = [CT | Rest]}
+ = Buffers,
+ connection_states = ConnStates0} = State) ->
+ case dtls_record:decode_cipher_text(CT, ConnStates0) of
+ {Plain, ConnStates} ->
+ {Plain, State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_cipher_texts = Rest},
+ connection_states = ConnStates}};
+ #alert{} = Alert ->
+ {Alert, State}
+ end;
+next_record(#state{socket = Socket,
+ transport_cb = Transport} = State) -> %% when FlightState =/= finished
+ ssl_socket:setopts(Transport, Socket, [{active,once}]),
+ {no_record, State};
+
+
+next_record(State) ->
+ {no_record, State}.
+
+next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) ->
+ handle_own_alert(Alert, Version, Current, State);
+
+next_state(_,Next, no_record, State) ->
+ {next_state, Next, State, get_timeout(State)};
+
+%% next_state(_,Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, State) ->
+%% Alerts = decode_alerts(EncAlerts),
+%% handle_alerts(Alerts, {next_state, Next, State, get_timeout(State)});
+
+next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
+ State0 = #state{protocol_buffers =
+ #protocol_buffers{dtls_handshake_buffer = Buf0} = Buffers,
+ negotiated_version = Version}) ->
+ Handle =
+ fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) ->
+ %% This message should not be included in handshake
+ %% message hashes. Starts new handshake (renegotiation)
+ Hs0 = ssl_handshake:init_handshake_history(),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs0,
+ renegotiation = {true, peer}});
+ ({#hello_request{} = Packet, _}, {next_state, SName, State}) ->
+ %% This message should not be included in handshake
+ %% message hashes. Already in negotiation so it will be ignored!
+ ?MODULE:SName(Packet, State);
+ ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) ->
+ Version = Packet#client_hello.client_version,
+ Hs0 = ssl_handshake:init_handshake_history(),
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1,
+ renegotiation = {true, peer}});
+ ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) ->
+ Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1});
+ (_, StopState) -> StopState
+ end,
+ try
+ {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0),
+ State = State0#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_packets = Packets,
+ dtls_handshake_buffer = Buf}},
+ handle_dtls_handshake(Handle, Next, State)
+ catch throw:#alert{} = Alert ->
+ handle_own_alert(Alert, Version, Current, State0)
+ end;
+
+next_state(_, StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State0) ->
+ %% Simplify for now to avoid dialzer warnings before implementation is compleate
+ %% case read_application_data(Data, State0) of
+ %% Stop = {stop,_,_} ->
+ %% Stop;
+ %% {Record, State} ->
+ %% next_state(StateName, StateName, Record, State)
+ %% end;
+ {Record, State} = read_application_data(Data, State0),
+ next_state(StateName, StateName, Record, State);
+
+next_state(Current, Next, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} =
+ _ChangeCipher,
+ #state{connection_states = ConnectionStates0} = State0) ->
+ ConnectionStates1 =
+ ssl_record:activate_pending_connection_state(ConnectionStates0, read),
+ {Record, State} = next_record(State0#state{connection_states = ConnectionStates1}),
+ next_state(Current, Next, Record, State);
+next_state(Current, Next, #ssl_tls{type = _Unknown}, State0) ->
+ %% Ignore unknown type
+ {Record, State} = next_record(State0),
+ next_state(Current, Next, Record, State).
+
+handle_dtls_handshake(Handle, StateName,
+ #state{protocol_buffers =
+ #protocol_buffers{dtls_packets = [Packet]} = Buffers} = State) ->
+ FsmReturn = {next_state, StateName, State#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_packets = []}}},
+ Handle(Packet, FsmReturn);
+
+handle_dtls_handshake(Handle, StateName,
+ #state{protocol_buffers =
+ #protocol_buffers{dtls_packets = [Packet | Packets]} = Buffers} =
+ State0) ->
+ FsmReturn = {next_state, StateName, State0#state{protocol_buffers =
+ Buffers#protocol_buffers{dtls_packets =
+ Packets}}},
+ case Handle(Packet, FsmReturn) of
+ {next_state, NextStateName, State, _Timeout} ->
+ handle_dtls_handshake(Handle, NextStateName, State);
+ {stop, _,_} = Stop ->
+ Stop
+ end.
+
+
+send_flight(Fragments, #state{transport_cb = Transport, socket = Socket,
+ protocol_buffers = _PBuffers} = State) ->
+ Transport:send(Socket, Fragments),
+ %% Start retransmission
+ %% State#state{protocol_buffers =
+ %% (PBuffers#protocol_buffers){ #flight{state = waiting}}}}.
+ State.
+
+handle_own_alert(_,_,_, State) -> %% Place holder
+ {stop, {shutdown, own_alert}, State}.
+
+handle_normal_shutdown(_, _, _State) -> %% Place holder
+ ok.
+
+encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
+ dtls_record:encode_change_cipher_spec(Version, ConnectionStates).
+
+initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
+ {CbModule, DataTag, CloseTag, ErrorTag}) ->
+ ConnectionStates = ssl_record:init_connection_states(Role),
+
+ SessionCacheCb = case application:get_env(ssl, session_cb) of
+ {ok, Cb} when is_atom(Cb) ->
+ Cb;
+ _ ->
+ ssl_session_cache
+ end,
+
+ Monitor = erlang:monitor(process, User),
+
+ #state{socket_options = SocketOptions,
+ %% We do not want to save the password in the state so that
+ %% could be written in the clear into error logs.
+ ssl_options = SSLOptions#ssl_options{password = undefined},
+ session = #session{is_resumable = new},
+ transport_cb = CbModule,
+ data_tag = DataTag,
+ close_tag = CloseTag,
+ error_tag = ErrorTag,
+ role = Role,
+ host = Host,
+ port = Port,
+ socket = Socket,
+ connection_states = ConnectionStates,
+ protocol_buffers = #protocol_buffers{},
+ user_application = {Monitor, User},
+ user_data_buffer = <<>>,
+ session_cache_cb = SessionCacheCb,
+ renegotiation = {false, first},
+ start_or_recv_from = undefined,
+ send_queue = queue:new(),
+ protocol_cb = ?MODULE
+ }.
+read_application_data(_,State) ->
+ {#ssl_tls{fragment = <<"place holder">>}, State}.
+
+next_tls_record(_, State) ->
+ {#ssl_tls{fragment = <<"place holder">>}, State}.
+
+get_timeout(_) -> %% Place holder
+ infinity.
+
+next_state_connection(_, State) -> %% Place holder
+ {next_state, connection, State, get_timeout(State)}.
+
+sequence(_) ->
+ %%TODO real imp
+ 1.
diff --git a/lib/ssl/src/dtls_connection.hrl b/lib/ssl/src/dtls_connection.hrl
index b8dff479d5..08707dc8de 100644
--- a/lib/ssl/src/dtls_connection.hrl
+++ b/lib/ssl/src/dtls_connection.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,24 +28,19 @@
-include("ssl_connection.hrl").
-record(protocol_buffers, {
- dtls_packets = [] ::[binary()], % Not yet handled decode ssl/tls packets.
- dtls_record_buffer :: binary(), % Buffer of incomplete records
- dtls_handshake_buffer :: binary(), % Buffer of incomplete handshakes
- dtls_cipher_texts :: [binary()],
- dtls_cipher_texts_next :: [binary()] % Received for Epoch not yet active
+ dtls_packets = [], %%::[binary()], % Not yet handled decode ssl/tls packets.
+ dtls_record_buffer = <<>>, %%:: binary(), % Buffer of incomplete records
+ dtls_handshake_buffer = <<>>, %%:: binary(), % Buffer of incomplete handshakes
+ dtls_cipher_texts = [], %%:: [binary()],
+ dtls_cipher_texts_next %%:: [binary()] % Received for Epoch not yet active
}).
-record(flight, {
last_retransmit,
last_read_seq,
msl_timer,
- flight_state,
- flight_buffer, % buffer of not yet ACKed TLS records
- }).
-
--record(message_sequences, {
- read = 0,
- write = 0
+ state,
+ buffer % buffer of not yet ACKed TLS records
}).
-endif. % -ifdef(dtls_connection).
diff --git a/lib/ssl/src/dtls_connection_sup.erl b/lib/ssl/src/dtls_connection_sup.erl
index 9fe545be18..0b4711cfb4 100644
--- a/lib/ssl/src/dtls_connection_sup.erl
+++ b/lib/ssl/src/dtls_connection_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,8 +26,8 @@
-behaviour(supervisor).
%% API
--export([start_link/0]).
--export([start_child/1]).
+-export([start_link/0, start_link_dist/0]).
+-export([start_child/1, start_child_dist/1]).
%% Supervisor callback
-export([init/1]).
@@ -38,8 +38,14 @@
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+start_link_dist() ->
+ supervisor:start_link({local, dtls_connection_sup_dist}, ?MODULE, []).
+
start_child(Args) ->
supervisor:start_child(?MODULE, Args).
+
+start_child_dist(Args) ->
+ supervisor:start_child(dtls_connection_sup_dist, Args).
%%%=========================================================================
%%% Supervisor callback
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 5db2434753..31d525b295 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,11 +20,15 @@
-include("dtls_handshake.hrl").
-include("dtls_record.hrl").
-include("ssl_internal.hrl").
+-include("ssl_alert.hrl").
--export([client_hello/8, client_hello/9, hello/3,
+-export([client_hello/8, client_hello/9, hello/4,
get_dtls_handshake/2,
- dtls_handshake_new_flight/1, dtls_handshake_new_epoch/1,
- encode_handshake/4]).
+ %%dtls_handshake_new_flight/1, dtls_handshake_new_epoch/1,
+ encode_handshake/3]).
+
+-type dtls_handshake() :: #client_hello{} | #hello_verify_request{} |
+ ssl_handshake:ssl_handshake().
%%====================================================================
%% Internal application API
@@ -54,12 +58,12 @@ client_hello(Host, Port, Cookie, ConnectionStates,
ciphers = UserSuites
} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
- Version = dtls_record:highest_protocol_version(Versions),
+ Version = dtls_record:highest_protocol_version(Versions),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
CipherSuites = ssl_handshake:available_suites(UserSuites, Version),
- Extensions = ssl_handshake:client_hello_extensions(Host, Version, CipherSuites,
+ Extensions = ssl_handshake:client_hello_extensions(Host, dtls_v1:corresponding_tls_version(Version), CipherSuites,
SslOpts, ConnectionStates, Renegotiation),
Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
@@ -73,163 +77,197 @@ client_hello(Host, Port, Cookie, ConnectionStates,
extensions = Extensions
}.
-hello(Address, Port,
- #ssl_tls{epoch = _Epoch, record_seq = _Seq,
- version = Version} = Record) ->
- {[{Hello, _}], _, _} =
- get_dtls_handshake(Record,
- dtls_handshake_new_flight(undefined)),
- #client_hello{client_version = {Major, Minor},
- random = Random,
- session_id = SessionId,
- cipher_suites = CipherSuites,
- compression_methods = CompressionMethods} = Hello,
- CookieData = [address_to_bin(Address, Port),
- <<?BYTE(Major), ?BYTE(Minor)>>,
- Random, SessionId, CipherSuites, CompressionMethods],
- Cookie = crypto:hmac(sha, <<"secret">>, CookieData),
-
- case Hello of
- #client_hello{cookie = Cookie} ->
- accept;
- _ ->
- %% generate HelloVerifyRequest
- HelloVerifyRequest = encode_handshake(#hello_verify_request{protocol_version = Version,
- cookie = Cookie},
- Version, 0, 1400),
- {reply, HelloVerifyRequest}
- end.
+hello(#server_hello{server_version = Version, random = Random,
+ cipher_suite = CipherSuite,
+ compression_method = Compression,
+ session_id = SessionId, extensions = HelloExt},
+ #ssl_options{versions = SupportedVersions} = SslOpt,
+ ConnectionStates0, Renegotiation) ->
+ case dtls_record:is_acceptable_version(Version, SupportedVersions) of
+ true ->
+ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
+ Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation);
+ false ->
+ ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
+ end;
-%%--------------------------------------------------------------------
-encode_handshake(Package, Version, MsgSeq, Mss) ->
- {MsgType, Bin} = enc_hs(Package, Version),
+hello(#client_hello{client_version = ClientVersion}, _Options, {_,_,_,_,ConnectionStates,_}, _Renegotiation) ->
+ %% Return correct typ to make dialyzer happy until we have time to make the real imp.
+ {ClientVersion, {new, #session{}}, ConnectionStates, #hello_extensions{}}.
+
+%% hello(Address, Port,
+%% #ssl_tls{epoch = _Epoch, sequence_number = _Seq,
+%% version = Version} = Record) ->
+%% case get_dtls_handshake(Record,
+%% dtls_handshake_new_flight(undefined)) of
+%% {[Hello | _], _} ->
+%% hello(Address, Port, Version, Hello);
+%% {retransmit, HandshakeState} ->
+%% {retransmit, HandshakeState}
+%% end.
+
+%% hello(Address, Port, Version, Hello) ->
+%% #client_hello{client_version = {Major, Minor},
+%% random = Random,
+%% session_id = SessionId,
+%% cipher_suites = CipherSuites,
+%% compression_methods = CompressionMethods} = Hello,
+%% CookieData = [address_to_bin(Address, Port),
+%% <<?BYTE(Major), ?BYTE(Minor)>>,
+%% Random, SessionId, CipherSuites, CompressionMethods],
+%% Cookie = crypto:hmac(sha, <<"secret">>, CookieData),
+
+%% case Hello of
+%% #client_hello{cookie = Cookie} ->
+%% accept;
+%% _ ->
+%% %% generate HelloVerifyRequest
+%% HelloVerifyRequest = enc_hs(#hello_verify_request{protocol_version = Version,
+%% cookie = Cookie},
+%% Version, 0, 1400),
+%% {reply, HelloVerifyRequest}
+%% end.
+
+%% %%--------------------------------------------------------------------
+encode_handshake(Handshake, Version, MsgSeq) ->
+ {MsgType, Bin} = enc_handshake(Handshake, Version),
Len = byte_size(Bin),
- HsHistory = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(0), ?uint24(Len), Bin],
- BinMsg = dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Bin, 0, []),
- {HsHistory, BinMsg}.
+ EncHandshake = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(0), ?uint24(Len), Bin],
+ FragmentedHandshake = dtls_fragment(erlang:iolist_size(EncHandshake), MsgType, Len, MsgSeq, Bin, 0, []),
+ {EncHandshake, FragmentedHandshake}.
-%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
-spec get_dtls_handshake(#ssl_tls{}, #dtls_hs_state{} | binary()) ->
- {[dtls_handshake()], #dtls_hs_state{}} | {retransmit, #dtls_hs_state{}}.
-%
-% Description: Given a DTLS state and new data from ssl_record, collects
-% and returns it as a list of handshake messages, also returns a new
-% DTLS state
-%--------------------------------------------------------------------
-% get_dtls_handshake(Record, <<>>) ->
-% get_dtls_handshake_aux(Record, dtls_hs_state_init());
+ {[dtls_handshake()], #dtls_hs_state{}} | {retransmit, #dtls_hs_state{}}.
+%%
+%% Description: Given a DTLS state and new data from ssl_record, collects
+%% and returns it as a list of handshake messages, also returns a new
+%% DTLS state
+%%--------------------------------------------------------------------
+get_dtls_handshake(Record, <<>>) ->
+ get_dtls_handshake_aux(Record, #dtls_hs_state{}); %% Init handshake state!?
get_dtls_handshake(Record, HsState) ->
get_dtls_handshake_aux(Record, HsState).
-%--------------------------------------------------------------------
--spec dtls_handshake_new_epoch(#dtls_hs_state{}) -> #dtls_hs_state{}.
-%
-% Description: Reset the DTLS decoder state for a new Epoch
-%--------------------------------------------------------------------
-% dtls_handshake_new_epoch(<<>>) ->
-% dtls_hs_state_init();
-dtls_handshake_new_epoch(HsState) ->
- HsState#dtls_hs_state{highest_record_seq = 0,
- starting_read_seq = HsState#dtls_hs_state.current_read_seq,
- fragments = gb_trees:empty(), completed = []}.
-
-%--------------------------------------------------------------------
--spec dtls_handshake_new_flight(integer() | undefined) -> #dtls_hs_state{}.
-%
-% Description: Init the DTLS decoder state for a new Flight
-dtls_handshake_new_flight(ExpectedReadReq) ->
- #dtls_hs_state{current_read_seq = ExpectedReadReq,
- highest_record_seq = 0,
- starting_read_seq = 0,
- fragments = gb_trees:empty(), completed = []}.
+%% %%--------------------------------------------------------------------
+%% -spec dtls_handshake_new_epoch(#dtls_hs_state{}) -> #dtls_hs_state{}.
+%% %%
+%% %% Description: Reset the DTLS decoder state for a new Epoch
+%% %%--------------------------------------------------------------------
+%% dtls_handshake_new_epoch(<<>>) ->
+%% dtls_hs_state_init();
+%% dtls_handshake_new_epoch(HsState) ->
+%% HsState#dtls_hs_state{highest_record_seq = 0,
+%% starting_read_seq = HsState#dtls_hs_state.current_read_seq,
+%% fragments = gb_trees:empty(), completed = []}.
+
+%% %--------------------------------------------------------------------
+%% -spec dtls_handshake_new_flight(integer() | undefined) -> #dtls_hs_state{}.
+%% %
+%% % Description: Init the DTLS decoder state for a new Flight
+%% dtls_handshake_new_flight(ExpectedReadReq) ->
+%% #dtls_hs_state{current_read_seq = ExpectedReadReq,
+%% highest_record_seq = 0,
+%% starting_read_seq = 0,
+%% fragments = gb_trees:empty(), completed = []}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
+ Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) ->
+ case ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite,
+ Compression, HelloExt, Version,
+ SslOpt, ConnectionStates0, Renegotiation) of
+ #alert{} = Alert ->
+ Alert;
+ {ConnectionStates, Protocol} ->
+ {Version, SessionId, ConnectionStates, Protocol}
+ end.
-dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc)
+dtls_fragment(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc)
when byte_size(Bin) + 12 < Mss ->
FragmentLen = byte_size(Bin),
BinMsg = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragmentLen), Bin],
lists:reverse([BinMsg|Acc]);
-dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc) ->
+dtls_fragment(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc) ->
FragmentLen = Mss - 12,
<<Fragment:FragmentLen/bytes, Rest/binary>> = Bin,
BinMsg = [MsgType, ?uint24(Len), ?uint16(MsgSeq), ?uint24(Offset), ?uint24(FragmentLen), Fragment],
- dtls_split_handshake(Mss, MsgType, Len, MsgSeq, Rest, Offset + FragmentLen, [BinMsg|Acc]).
+ dtls_fragment(Mss, MsgType, Len, MsgSeq, Rest, Offset + FragmentLen, [BinMsg|Acc]).
get_dtls_handshake_aux(#ssl_tls{version = Version,
- record_seq = SeqNo,
- fragment = Data}, HsState) ->
+ sequence_number = SeqNo,
+ fragment = Data}, HsState) ->
get_dtls_handshake_aux(Version, SeqNo, Data, HsState).
get_dtls_handshake_aux(Version, SeqNo,
- <<?BYTE(Type), ?UINT24(Length),
- ?UINT16(MessageSeq),
- ?UINT24(FragmentOffset), ?UINT24(FragmentLength),
- Body:FragmentLength/binary, Rest/binary>>,
- HsState0) ->
+ <<?BYTE(Type), ?UINT24(Length),
+ ?UINT16(MessageSeq),
+ ?UINT24(FragmentOffset), ?UINT24(FragmentLength),
+ Body:FragmentLength/binary, Rest/binary>>,
+ HsState0) ->
case reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, HsState0) of
- {retransmit, HsState1} ->
- case Rest of
- <<>> ->
- {retransmit, HsState1};
- _ ->
- get_dtls_handshake_aux(Version, SeqNo, Rest, HsState1)
- end;
- {HsState1, HighestSeqNo, MsgBody} ->
- HsState2 = dec_dtls_fragment(Version, HighestSeqNo, Type, Length, MessageSeq, MsgBody, HsState1),
- HsState3 = process_dtls_fragments(Version, HsState2),
- get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3);
- HsState2 ->
- HsState3 = process_dtls_fragments(Version, HsState2),
- get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3)
- end;
+ FragmentOffset, FragmentLength,
+ Body, HsState0) of
+ {retransmit, HsState1} ->
+ case Rest of
+ <<>> ->
+ {retransmit, HsState1};
+ _ ->
+ get_dtls_handshake_aux(Version, SeqNo, Rest, HsState1)
+ end;
+ {HsState1, HighestSeqNo, MsgBody} ->
+ HsState2 = dec_dtls_fragment(Version, HighestSeqNo, Type, Length, MessageSeq, MsgBody, HsState1),
+ HsState3 = process_dtls_fragments(Version, HsState2),
+ get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3);
+ HsState2 ->
+ HsState3 = process_dtls_fragments(Version, HsState2),
+ get_dtls_handshake_aux(Version, SeqNo, Rest, HsState3)
+ end;
get_dtls_handshake_aux(_Version, _SeqNo, <<>>, HsState) ->
- {lists:reverse(HsState#dtls_hs_state.completed),
- HsState#dtls_hs_state{completed = []}}.
+ {lists:reverse(HsState#dtls_hs_state.completed),
+ HsState#dtls_hs_state{completed = []}}.
dec_dtls_fragment(Version, SeqNo, Type, Length, MessageSeq, MsgBody,
- HsState = #dtls_hs_state{highest_record_seq = HighestSeqNo, completed = Acc}) ->
+ HsState = #dtls_hs_state{highest_record_seq = HighestSeqNo, completed = Acc}) ->
Raw = <<?BYTE(Type), ?UINT24(Length), ?UINT16(MessageSeq), ?UINT24(0), ?UINT24(Length), MsgBody/binary>>,
H = decode_handshake(Version, Type, MsgBody),
HsState#dtls_hs_state{completed = [{H,Raw}|Acc], highest_record_seq = erlang:max(HighestSeqNo, SeqNo)}.
process_dtls_fragments(Version,
- HsState0 = #dtls_hs_state{current_read_seq = CurrentReadSeq,
- fragments = Fragments0}) ->
+ HsState0 = #dtls_hs_state{current_read_seq = CurrentReadSeq,
+ fragments = Fragments0}) ->
case gb_trees:is_empty(Fragments0) of
- true ->
- HsState0;
- _ ->
- case gb_trees:smallest(Fragments0) of
- {CurrentReadSeq, {SeqNo, Type, Length, CurrentReadSeq, {Length, [{0, Length}], MsgBody}}} ->
- HsState1 = dtls_hs_state_process_seq(HsState0),
- HsState2 = dec_dtls_fragment(Version, SeqNo, Type, Length, CurrentReadSeq, MsgBody, HsState1),
- process_dtls_fragments(Version, HsState2);
- _ ->
- HsState0
- end
- end.
+ true ->
+ HsState0;
+ _ ->
+ case gb_trees:smallest(Fragments0) of
+ {CurrentReadSeq, {SeqNo, Type, Length, CurrentReadSeq, {Length, [{0, Length}], MsgBody}}} ->
+ HsState1 = dtls_hs_state_process_seq(HsState0),
+ HsState2 = dec_dtls_fragment(Version, SeqNo, Type, Length, CurrentReadSeq, MsgBody, HsState1),
+ process_dtls_fragments(Version, HsState2);
+ _ ->
+ HsState0
+ end
+ end.
dtls_hs_state_process_seq(HsState0 = #dtls_hs_state{current_read_seq = CurrentReadSeq,
- fragments = Fragments0}) ->
+ fragments = Fragments0}) ->
Fragments1 = gb_trees:delete_any(CurrentReadSeq, Fragments0),
HsState0#dtls_hs_state{current_read_seq = CurrentReadSeq + 1,
- fragments = Fragments1}.
+ fragments = Fragments1}.
dtls_hs_state_add_fragment(MessageSeq, Fragment, HsState0 = #dtls_hs_state{fragments = Fragments0}) ->
Fragments1 = gb_trees:enter(MessageSeq, Fragment, Fragments0),
HsState0#dtls_hs_state{fragments = Fragments1}.
reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq, 0, Length,
- Body, HsState0 = #dtls_hs_state{current_read_seq = undefined})
+ Body, HsState0 = #dtls_hs_state{current_read_seq = undefined})
when Type == ?CLIENT_HELLO;
Type == ?SERVER_HELLO;
- Type == ?HELLO_VERIFY_REQUEST ->
+ Type == ?HELLO_VERIFY_REQUEST ->
%% First message, should be client hello
%% return the current message and set the next expected Sequence
%%
@@ -245,8 +283,8 @@ reassemble_dtls_fragment(_SeqNo, _Type, Length, _MessageSeq, _, Length,
HsState;
reassemble_dtls_fragment(SeqNo, _Type, Length, MessageSeq, 0, Length,
- Body, HsState0 =
- #dtls_hs_state{starting_read_seq = StartingReadSeq})
+ Body, HsState0 =
+ #dtls_hs_state{starting_read_seq = StartingReadSeq})
when MessageSeq < StartingReadSeq ->
%% this has to be the start of a new flight, let it through
%%
@@ -257,69 +295,69 @@ reassemble_dtls_fragment(SeqNo, _Type, Length, MessageSeq, 0, Length,
{HsState, SeqNo, Body};
reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, 0, Length,
- _Body, HsState =
- #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ _Body, HsState =
+ #dtls_hs_state{current_read_seq = CurrentReadSeq})
when MessageSeq < CurrentReadSeq ->
{retransmit, HsState};
reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, 0, Length,
- _Body, HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ _Body, HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
when MessageSeq < CurrentReadSeq ->
HsState;
reassemble_dtls_fragment(SeqNo, _Type, Length, MessageSeq, 0, Length,
- Body, HsState0 = #dtls_hs_state{current_read_seq = MessageSeq}) ->
+ Body, HsState0 = #dtls_hs_state{current_read_seq = MessageSeq}) ->
%% Message fully contained and it's the current seq
HsState1 = dtls_hs_state_process_seq(HsState0),
{HsState1, SeqNo, Body};
reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq, 0, Length,
- Body, HsState) ->
+ Body, HsState) ->
%% Message fully contained and it's the NOT the current seq -> buffer
Fragment = {SeqNo, Type, Length, MessageSeq,
- dtls_fragment_init(Length, 0, Length, Body)},
+ dtls_fragment_init(Length, 0, Length, Body)},
dtls_hs_state_add_fragment(MessageSeq, Fragment, HsState);
reassemble_dtls_fragment(_SeqNo, _Type, Length, MessageSeq, FragmentOffset, FragmentLength,
- _Body,
- HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ _Body,
+ HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
when FragmentOffset + FragmentLength == Length andalso MessageSeq == (CurrentReadSeq - 1) ->
{retransmit, HsState};
reassemble_dtls_fragment(_SeqNo, _Type, _Length, MessageSeq, _FragmentOffset, _FragmentLength,
- _Body,
- HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
+ _Body,
+ HsState = #dtls_hs_state{current_read_seq = CurrentReadSeq})
when MessageSeq < CurrentReadSeq ->
HsState;
reassemble_dtls_fragment(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body,
- HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ FragmentOffset, FragmentLength,
+ Body,
+ HsState = #dtls_hs_state{fragments = Fragments0}) ->
case gb_trees:lookup(MessageSeq, Fragments0) of
- {value, Fragment} ->
- dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, Fragment, HsState);
- none ->
- dtls_fragment_start(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, HsState)
+ {value, Fragment} ->
+ dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
+ FragmentOffset, FragmentLength,
+ Body, Fragment, HsState);
+ none ->
+ dtls_fragment_start(SeqNo, Type, Length, MessageSeq,
+ FragmentOffset, FragmentLength,
+ Body, HsState)
end.
dtls_fragment_start(SeqNo, Type, Length, MessageSeq,
- FragmentOffset, FragmentLength,
- Body, HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ FragmentOffset, FragmentLength,
+ Body, HsState = #dtls_hs_state{fragments = Fragments0}) ->
Fragment = {SeqNo, Type, Length, MessageSeq,
- dtls_fragment_init(Length, FragmentOffset, FragmentLength, Body)},
- Fragments1 = gb_trees:insert(MessageSeq, Fragment, Fragments0),
+ dtls_fragment_init(Length, FragmentOffset, FragmentLength, Body)},
+ Fragments1 = gb_trees:insert(MessageSeq, Fragment, Fragments0),
HsState#dtls_hs_state{fragments = Fragments1}.
dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
FragmentOffset, FragmentLength,
- Body,
- {LastSeqNo, Type, Length, MessageSeq, FragBuffer0},
- HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ Body,
+ {LastSeqNo, Type, Length, MessageSeq, FragBuffer0},
+ HsState = #dtls_hs_state{fragments = Fragments0}) ->
FragBuffer1 = dtls_fragment_add(FragBuffer0, FragmentOffset, FragmentLength, Body),
Fragment = {erlang:max(SeqNo, LastSeqNo), Type, Length, MessageSeq, FragBuffer1},
Fragments1 = gb_trees:enter(MessageSeq, Fragment, Fragments0),
@@ -328,8 +366,8 @@ dtls_fragment_reassemble(SeqNo, Type, Length, MessageSeq,
%% Type, Length or Seq mismatch, drop everything...
%% Note: the RFC is not clear on how to handle this...
dtls_fragment_reassemble(_SeqNo, _Type, _Length, MessageSeq,
- _FragmentOffset, _FragmentLength, _Body, _Fragment,
- HsState = #dtls_hs_state{fragments = Fragments0}) ->
+ _FragmentOffset, _FragmentLength, _Body, _Fragment,
+ HsState = #dtls_hs_state{fragments = Fragments0}) ->
Fragments1 = gb_trees:delete_any(MessageSeq, Fragments0),
HsState#dtls_hs_state{fragments = Fragments1}.
@@ -360,7 +398,7 @@ merge_fragment_list(Rest = [{HStart, _HEnd}|_], Frag = {_FStart, FEnd}, Acc)
lists:reverse(Acc) ++ [Frag|Rest];
merge_fragment_list([{HStart, HEnd}|Rest], _Frag = {FStart, FEnd}, Acc)
- when
+ when
FStart =< HEnd orelse FEnd >= HStart ->
Start = erlang:min(HStart, FStart),
End = erlang:max(HEnd, FEnd),
@@ -370,20 +408,20 @@ merge_fragment_list([{HStart, HEnd}|Rest], _Frag = {FStart, FEnd}, Acc)
add_fragment(List, {FragmentOffset, FragmentLength}) ->
merge_fragment_list(List, {FragmentOffset, FragmentOffset + FragmentLength}, []).
-enc_hs(#hello_verify_request{protocol_version = {Major, Minor},
- cookie = Cookie}, _Version) ->
- CookieLength = byte_size(Cookie),
+enc_handshake(#hello_verify_request{protocol_version = {Major, Minor},
+ cookie = Cookie}, _Version) ->
+ CookieLength = byte_size(Cookie),
{?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
- ?BYTE(CookieLength),
- Cookie/binary>>};
-
-enc_hs(#client_hello{client_version = {Major, Minor},
- random = Random,
- session_id = SessionID,
- cookie = Cookie,
- cipher_suites = CipherSuites,
- compression_methods = CompMethods,
- extensions = HelloExtensions}, Version) ->
+ ?BYTE(CookieLength),
+ Cookie/binary>>};
+
+enc_handshake(#client_hello{client_version = {Major, Minor},
+ random = Random,
+ session_id = SessionID,
+ cookie = Cookie,
+ cipher_suites = CipherSuites,
+ compression_methods = CompMethods,
+ extensions = HelloExtensions}, Version) ->
SIDLength = byte_size(SessionID),
BinCookie = enc_client_hello_cookie(Version, Cookie),
BinCompMethods = list_to_binary(CompMethods),
@@ -391,13 +429,13 @@ enc_hs(#client_hello{client_version = {Major, Minor},
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions),
-
+
{?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
- ?BYTE(SIDLength), SessionID/binary,
- BinCookie/binary,
- ?UINT16(CsLength), BinCipherSuites/binary,
- ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
-enc_hs(HandshakeMsg, Version) ->
+ ?BYTE(SIDLength), SessionID/binary,
+ BinCookie/binary,
+ ?UINT16(CsLength), BinCipherSuites/binary,
+ ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
+enc_handshake(HandshakeMsg, Version) ->
ssl_handshake:encode_handshake(HandshakeMsg, Version).
enc_client_hello_cookie(_, <<>>) ->
@@ -407,26 +445,26 @@ enc_client_hello_cookie(_, Cookie) ->
<<?BYTE(CookieLength), Cookie/binary>>.
decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
- ?BYTE(SID_length), Session_ID:SID_length/binary,
- ?BYTE(Cookie_length), Cookie:Cookie_length/binary,
- ?UINT16(Cs_length), CipherSuites:Cs_length/binary,
- ?BYTE(Cm_length), Comp_methods:Cm_length/binary,
- Extensions/binary>>) ->
-
+ ?BYTE(SID_length), Session_ID:SID_length/binary,
+ ?BYTE(Cookie_length), Cookie:Cookie_length/binary,
+ ?UINT16(Cs_length), CipherSuites:Cs_length/binary,
+ ?BYTE(Cm_length), Comp_methods:Cm_length/binary,
+ Extensions/binary>>) ->
+
DecodedExtensions = ssl_handshake:decode_hello_extensions(Extensions),
-
+
#client_hello{
client_version = {Major,Minor},
random = Random,
- session_id = Session_ID,
+ session_id = Session_ID,
cookie = Cookie,
cipher_suites = ssl_handshake:decode_suites('2_bytes', CipherSuites),
compression_methods = Comp_methods,
extensions = DecodedExtensions
- };
+ };
decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
- ?BYTE(CookieLength), Cookie:CookieLength/binary>>) ->
+ ?BYTE(CookieLength), Cookie:CookieLength/binary>>) ->
#hello_verify_request{
protocol_version = {Major,Minor},
@@ -434,7 +472,7 @@ decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor),
decode_handshake(Version, Tag, Msg) ->
ssl_handshake:decode_handshake(Version, Tag, Msg).
-address_to_bin({A,B,C,D}, Port) ->
- <<0:80,16#ffff:16,A,B,C,D,Port:16>>;
-address_to_bin({A,B,C,D,E,F,G,H}, Port) ->
- <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
+%% address_to_bin({A,B,C,D}, Port) ->
+%% <<0:80,16#ffff:16,A,B,C,D,Port:16>>;
+%% address_to_bin({A,B,C,D,E,F,G,H}, Port) ->
+%% <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>.
diff --git a/lib/ssl/src/dtls_handshake.hrl b/lib/ssl/src/dtls_handshake.hrl
index 5bdf45f627..3b57575b6d 100644
--- a/lib/ssl/src/dtls_handshake.hrl
+++ b/lib/ssl/src/dtls_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -53,6 +53,4 @@
completed
}).
--type dtls_handshake() :: #client_hello{} | #hello_verify_request{} | ssl_handshake().
-
-endif. % -ifdef(dtls_handshake).
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index b0a7976864..a7bbb6bc40 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,7 +35,7 @@
-export([decode_cipher_text/2]).
%% Encoding
--export([encode_plain_text/4]).
+-export([encode_plain_text/4, encode_handshake/3, encode_change_cipher_spec/2]).
%% Protocol version handling
-export([protocol_version/1, lowest_protocol_version/2,
@@ -46,6 +46,11 @@
-export([init_connection_state_seq/2, current_connection_state_epoch/2,
set_connection_state_by_epoch/3, connection_state_by_epoch/3]).
+-export_type([dtls_version/0, dtls_atom_version/0]).
+
+-type dtls_version() :: ssl_record:ssl_version().
+-type dtls_atom_version() :: dtlsv1 | 'dtlsv1.2'.
+
-compile(inline).
%%====================================================================
@@ -70,7 +75,7 @@ get_dtls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer),
Acc) ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
@@ -78,7 +83,7 @@ get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
Data:Length/binary, Rest/binary>>, Acc) when MajVer >= 128 ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
@@ -86,7 +91,7 @@ get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
Rest/binary>>, Acc) ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?ALERT,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
@@ -94,7 +99,7 @@ get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
Acc) ->
get_dtls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
version = {MajVer, MinVer},
- epoch = Epoch, record_seq = SequenceNumber,
+ epoch = Epoch, sequence_number = SequenceNumber,
fragment = Data} | Acc]);
get_dtls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer),
@@ -125,14 +130,15 @@ encode_plain_text(Type, Version, Data,
{Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0),
WriteState1 = WriteState0#connection_state{compression_state = CompS1},
MacHash = calc_mac_hash(WriteState1, Type, Version, Epoch, Seq, Comp),
- {CipherFragment, WriteState} = ssl_record:cipher(Version, Comp, WriteState1, MacHash),
+ {CipherFragment, WriteState} = ssl_record:cipher(dtls_v1:corresponding_tls_version(Version),
+ Comp, WriteState1, MacHash),
CipherText = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherFragment),
{CipherText, ConnectionStates#connection_states{current_write =
WriteState#connection_state{sequence_number = Seq +1}}}.
decode_cipher_text(#ssl_tls{type = Type, version = Version,
epoch = Epoch,
- record_seq = Seq,
+ sequence_number = Seq,
fragment = CipherFragment} = CipherText,
#connection_states{current_read =
#connection_state{compression_state = CompressionS0,
@@ -141,7 +147,7 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
CompressAlg = SecParams#security_parameters.compression_algorithm,
{PlainFragment, Mac, ReadState1} = ssl_record:decipher(dtls_v1:corresponding_tls_version(Version),
CipherFragment, ReadState0),
- MacHash = calc_mac_hash(Type, Version, Epoch, Seq, PlainFragment, ReadState1),
+ MacHash = calc_mac_hash(ReadState1, Type, Version, Epoch, Seq, PlainFragment),
case ssl_record:is_correct_mac(Mac, MacHash) of
true ->
{Plain, CompressionS1} = ssl_record:uncompress(CompressAlg,
@@ -153,10 +159,27 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
false ->
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
end.
+%%--------------------------------------------------------------------
+-spec encode_handshake(iolist(), dtls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes a handshake message to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_handshake(Frag, Version, ConnectionStates) ->
+ encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
+
+%%--------------------------------------------------------------------
+-spec encode_change_cipher_spec(dtls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
+%%--------------------------------------------------------------------
+encode_change_cipher_spec(Version, ConnectionStates) ->
+ encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
%%--------------------------------------------------------------------
--spec protocol_version(tls_atom_version() | tls_version()) ->
- tls_version() | tls_atom_version().
+-spec protocol_version(dtls_atom_version() | dtls_version()) ->
+ dtls_version() | dtls_atom_version().
%%
%% Description: Creates a protocol version record from a version atom
%% or vice versa.
@@ -170,7 +193,7 @@ protocol_version({254, 253}) ->
protocol_version({254, 255}) ->
dtlsv1.
%%--------------------------------------------------------------------
--spec lowest_protocol_version(tls_version(), tls_version()) -> tls_version().
+-spec lowest_protocol_version(dtls_version(), dtls_version()) -> dtls_version().
%%
%% Description: Lowes protocol version of two given versions
%%--------------------------------------------------------------------
@@ -183,7 +206,7 @@ lowest_protocol_version(Version = {M,_}, {N, _}) when M > N ->
lowest_protocol_version(_,Version) ->
Version.
%%--------------------------------------------------------------------
--spec highest_protocol_version([tls_version()]) -> tls_version().
+-spec highest_protocol_version([dtls_version()]) -> dtls_version().
%%
%% Description: Highest protocol version present in a list
%%--------------------------------------------------------------------
@@ -203,7 +226,7 @@ highest_protocol_version(_, [Version | Rest]) ->
%%--------------------------------------------------------------------
--spec supported_protocol_versions() -> [tls_version()].
+-spec supported_protocol_versions() -> [dtls_version()].
%%
%% Description: Protocol versions supported
%%--------------------------------------------------------------------
@@ -234,7 +257,7 @@ supported_connection_protocol_versions([]) ->
?ALL_DATAGRAM_SUPPORTED_VERSIONS.
%%--------------------------------------------------------------------
--spec is_acceptable_version(tls_version(), Supported :: [tls_version()]) -> boolean().
+-spec is_acceptable_version(dtls_version(), Supported :: [dtls_version()]) -> boolean().
%%
%% Description: ssl version 2 is not acceptable security risks are too big.
%%
@@ -244,7 +267,7 @@ is_acceptable_version(Version, Versions) ->
%%--------------------------------------------------------------------
--spec init_connection_state_seq(tls_version(), #connection_states{}) ->
+-spec init_connection_state_seq(dtls_version(), #connection_states{}) ->
#connection_state{}.
%%
%% Description: Copy the read sequence number to the write sequence number
@@ -343,5 +366,5 @@ calc_mac_hash(#connection_state{mac_secret = MacSecret,
Length, Fragment).
mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
- dtls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
+ dtls_v1:mac_hash(Version, MacAlg, MacSecret, SeqNo, Type,
Length, Fragment).
diff --git a/lib/ssl/src/dtls_record.hrl b/lib/ssl/src/dtls_record.hrl
index e935d84bdf..edb77fb2b1 100644
--- a/lib/ssl/src/dtls_record.hrl
+++ b/lib/ssl/src/dtls_record.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,16 +28,15 @@
-include("ssl_record.hrl"). %% Common TLS and DTLS records and Constantes
-%% Used to handle tls_plain_text, tls_compressed and tls_cipher_text
+%% Used to handle dtls_plain_text, dtls_compressed and dtls_cipher_text
-record(ssl_tls, {
type,
version,
- record_seq, % used in plain_text
- epoch, % used in plain_text
- message_seq,
- fragment_offset,
- fragment_length,
+ epoch,
+ sequence_number,
+ offset,
+ length,
fragment
}).
diff --git a/lib/ssl/src/dtls_v1.erl b/lib/ssl/src/dtls_v1.erl
index 6e41641483..5a7ab32887 100644
--- a/lib/ssl/src/dtls_v1.erl
+++ b/lib/ssl/src/dtls_v1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-export([suites/1, mac_hash/7, ecc_curves/1, corresponding_tls_version/1]).
--spec suites(Minor:: 253|255) -> [cipher_suite()].
+-spec suites(Minor:: 253|255) -> [ssl_cipher:cipher_suite()].
suites(Minor) ->
tls_v1:suites(corresponding_minor_tls_version(Minor)).
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 68ebc49e4a..36681e2897 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -28,6 +28,7 @@
ssl_srp_primes,
ssl_alert,
ssl_socket,
+ ssl_listen_tracker_sup,
%% Erlang Distribution over SSL/TLS
inet_tls_dist,
ssl_tls_dist_proxy,
@@ -47,6 +48,8 @@
{registered, [ssl_sup, ssl_manager]},
{applications, [crypto, public_key, kernel, stdlib]},
{env, []},
- {mod, {ssl_app, []}}]}.
+ {mod, {ssl_app, []}},
+ {runtime_dependencies, ["stdlib-2.0","public_key-0.22","kernel-3.0",
+ "erts-6.0","crypto-3.3"]}]}.
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index b0ef292c4e..b713f86c1e 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,33 +1,13 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"5.3.3", [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, ssl_handshake, soft_purge, soft_purge, []},
- {load_module, tls_handshake, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []}]},
- {"5.3.2", [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, ssl_handshake, soft_purge, soft_purge, []},
- {load_module, tls_handshake, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []}]},
- {<<"5\\.3\\.1($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.3\\.[1-4]($|\\..*)">>, [{restart_application, ssl}]},
{<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
- {"5.3.3", [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, ssl_handshake, soft_purge, soft_purge, []},
- {load_module, tls_handshake, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []}]},
- {"5.3.2", [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, ssl_handshake, soft_purge, soft_purge, []},
- {load_module, tls_handshake, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []}]},
- {<<"5\\.3\\.1($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.3\\.[1-4]($|\\..*)">>, [{restart_application, ssl}]},
{<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index c3bdeb1a54..be1041ca13 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -97,17 +97,17 @@ connect(Socket, SslOptions) when is_port(Socket) ->
connect(Socket, SslOptions0, Timeout) when is_port(Socket) ->
{Transport,_,_,_} = proplists:get_value(cb_info, SslOptions0,
{gen_tcp, tcp, tcp_closed, tcp_error}),
- EmulatedOptions = emulated_options(),
+ EmulatedOptions = ssl_socket:emulated_options(),
{ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
- try handle_options(SslOptions0 ++ SocketValues, client) of
+ try handle_options(SslOptions0 ++ SocketValues) of
{ok, #config{transport_info = CbInfo, ssl = SslOptions, emulated = EmOpts,
connection_cb = ConnectionCb}} ->
- ok = ssl_socket:setopts(Transport, Socket, internal_inet_values()),
+ 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, EmOpts},
+ {SslOptions, emulated_socket_options(EmOpts, #socket_options{}), undefined},
self(), CbInfo, Timeout);
{error, Error} ->
{error, Error}
@@ -121,7 +121,7 @@ connect(Host, Port, Options) ->
connect(Host, Port, Options, infinity).
connect(Host, Port, Options, Timeout) ->
- try handle_options(Options, client) of
+ try handle_options(Options) of
{ok, Config} ->
do_connect(Host,Port,Config,Timeout)
catch
@@ -139,12 +139,15 @@ listen(_Port, []) ->
{error, nooptions};
listen(Port, Options0) ->
try
- {ok, Config} = handle_options(Options0, server),
+ {ok, Config} = handle_options(Options0),
ConnectionCb = connection_cb(Options0),
- #config{transport_info = {Transport, _, _, _}, inet_user = Options, connection_cb = ConnectionCb} = Config,
+ #config{transport_info = {Transport, _, _, _}, inet_user = Options, connection_cb = ConnectionCb,
+ ssl = SslOpts, emulated = EmOpts} = Config,
case Transport:listen(Port, Options) of
{ok, ListenSocket} ->
- {ok, #sslsocket{pid = {ListenSocket, Config}}};
+ 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
@@ -164,25 +167,20 @@ transport_accept(ListenSocket) ->
transport_accept(ListenSocket, infinity).
transport_accept(#sslsocket{pid = {ListenSocket,
- #config{transport_info = CbInfo,
+ #config{transport_info = {Transport,_,_, _} =CbInfo,
connection_cb = ConnectionCb,
- ssl = SslOpts}}}, Timeout) ->
- %% The setopt could have been invoked on the listen socket
- %% and options should be inherited.
- EmOptions = emulated_options(),
- {Transport,_,_, _} = CbInfo,
- {ok, SocketValues} = ssl_socket:getopts(Transport, ListenSocket, EmOptions),
- ok = ssl_socket:setopts(Transport, ListenSocket, internal_inet_values()),
+ ssl = SslOpts,
+ emulated = Tracker}}}, Timeout) ->
case Transport:accept(ListenSocket, Timeout) of
{ok, Socket} ->
- ok = ssl_socket:setopts(Transport, ListenSocket, SocketValues),
+ {ok, EmOpts} = ssl_socket:get_emulated_opts(Tracker),
{ok, Port} = ssl_socket:port(Transport, Socket),
ConnArgs = [server, "localhost", Port, Socket,
- {SslOpts, socket_options(SocketValues)}, self(), CbInfo],
+ {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);
+ ssl_connection:socket_control(ConnectionCb, Socket, Pid, Transport, Tracker);
{error, Reason} ->
{error, Reason}
end;
@@ -195,7 +193,8 @@ transport_accept(#sslsocket{pid = {ListenSocket,
-spec ssl_accept(#sslsocket{} | port(), timeout()| [ssl_option()
| transport_option()]) ->
ok | {ok, #sslsocket{}} | {error, reason()}.
--spec ssl_accept(port(), [ssl_option()| transport_option()], timeout()) ->
+
+-spec ssl_accept(#sslsocket{} | port(), [ssl_option()] | [ssl_option()| transport_option()], timeout()) ->
{ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Performs accept on an ssl listen socket. e.i. performs
@@ -210,19 +209,29 @@ ssl_accept(#sslsocket{} = Socket, Timeout) ->
ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
ssl_accept(ListenSocket, SslOptions, infinity).
+ssl_accept(#sslsocket{} = Socket, [], Timeout) ->
+ ssl_accept(#sslsocket{} = Socket, Timeout);
+ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts0, Timeout) ->
+ 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)
+ catch
+ Error = {error, _Reason} -> Error
+ end;
ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
{Transport,_,_,_} =
proplists:get_value(cb_info, SslOptions, {gen_tcp, tcp, tcp_closed, tcp_error}),
- EmulatedOptions = emulated_options(),
+ EmulatedOptions = ssl_socket:emulated_options(),
{ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
ConnetionCb = connection_cb(SslOptions),
- try handle_options(SslOptions ++ SocketValues, server) of
+ try handle_options(SslOptions ++ SocketValues) of
{ok, #config{transport_info = CbInfo, ssl = SslOpts, emulated = EmOpts}} ->
- ok = ssl_socket:setopts(Transport, Socket, internal_inet_values()),
+ ok = ssl_socket:setopts(Transport, Socket, ssl_socket:internal_inet_values()),
{ok, Port} = ssl_socket:port(Transport, Socket),
ssl_connection:ssl_accept(ConnetionCb, Port, Socket,
- {SslOpts, EmOpts},
- self(), CbInfo, Timeout)
+ {SslOpts, emulated_socket_options(EmOpts, #socket_options{}), undefined},
+ self(), CbInfo, Timeout)
catch
Error = {error, _Reason} -> Error
end.
@@ -276,7 +285,7 @@ controlling_process(#sslsocket{pid = {Listen,
Transport:controlling_process(Listen, NewOwner).
%%--------------------------------------------------------------------
--spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} |
+-spec connection_info(#sslsocket{}) -> {ok, {tls_record:tls_atom_version(), ssl_cipher:erl_cipher_suite()}} |
{error, reason()}.
%%
%% Description: Returns ssl protocol and cipher used for the connection
@@ -291,7 +300,7 @@ connection_info(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
%%
%% Description: same as inet:peername/1.
%%--------------------------------------------------------------------
-peername(#sslsocket{pid = Pid, fd = {Transport, Socket, _}}) when is_pid(Pid)->
+peername(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}) when is_pid(Pid)->
ssl_socket:peername(Transport, Socket);
peername(#sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_}}}}) ->
ssl_socket:peername(Transport, ListenSocket). %% Will return {error, enotconn}
@@ -312,7 +321,7 @@ peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
{error, enotconn}.
%%--------------------------------------------------------------------
--spec suite_definition(cipher_suite()) -> erl_cipher_suite().
+-spec suite_definition(ssl_cipher:cipher_suite()) -> ssl_cipher:erl_cipher_suite().
%%
%% Description: Return erlang cipher suite definition.
%%--------------------------------------------------------------------
@@ -330,28 +339,29 @@ negotiated_next_protocol(#sslsocket{pid = Pid}) ->
ssl_connection:negotiated_next_protocol(Pid).
%%--------------------------------------------------------------------
--spec cipher_suites() -> [erl_cipher_suite()].
--spec cipher_suites(erlang | openssl | all) -> [erl_cipher_suite()] | [string()].
+-spec cipher_suites() -> [ssl_cipher:erl_cipher_suite()].
+-spec cipher_suites(erlang | openssl | all) -> [ssl_cipher:erl_cipher_suite()] | [string()].
%% Description: Returns all supported cipher suites.
%%--------------------------------------------------------------------
cipher_suites() ->
cipher_suites(erlang).
-
+
cipher_suites(erlang) ->
Version = tls_record:highest_protocol_version([]),
- [suite_definition(S) || S <- ssl_cipher:suites(Version)];
-
+ ssl_cipher:filter_suites([suite_definition(S)
+ || S <- ssl_cipher:suites(Version)]);
cipher_suites(openssl) ->
Version = tls_record:highest_protocol_version([]),
- [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)];
+ [ssl_cipher:openssl_suite_name(S)
+ || S <- ssl_cipher:filter_suites(ssl_cipher:suites(Version))];
cipher_suites(all) ->
Version = tls_record:highest_protocol_version([]),
- Supported = ssl_cipher:suites(Version)
- ++ ssl_cipher:anonymous_suites()
+ Supported = ssl_cipher:all_suites(Version)
+ ++ ssl_cipher:anonymous_suites(Version)
++ ssl_cipher:psk_suites(Version)
++ ssl_cipher:srp_suites(),
- [suite_definition(S) || S <- Supported].
+ ssl_cipher:filter_suites([suite_definition(S) || S <- Supported]).
%%--------------------------------------------------------------------
-spec getopts(#sslsocket{}, [gen_tcp:option_name()]) ->
@@ -361,7 +371,7 @@ cipher_suites(all) ->
%%--------------------------------------------------------------------
getopts(#sslsocket{pid = Pid}, OptionTags) when is_pid(Pid), is_list(OptionTags) ->
ssl_connection:get_opts(Pid, OptionTags);
-getopts(#sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_}}}},
+getopts(#sslsocket{pid = {_, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket,
OptionTags) when is_list(OptionTags) ->
try ssl_socket:getopts(Transport, ListenSocket, OptionTags) of
{ok, _} = Result ->
@@ -369,8 +379,8 @@ getopts(#sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_
{error, InetError} ->
{error, {options, {socket_options, OptionTags, InetError}}}
catch
- _:_ ->
- {error, {options, {socket_options, OptionTags}}}
+ _:Error ->
+ {error, {options, {socket_options, OptionTags, Error}}}
end;
getopts(#sslsocket{}, OptionTags) ->
{error, {options, {socket_options, OptionTags}}}.
@@ -390,7 +400,7 @@ setopts(#sslsocket{pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) ->
{error, {options, {not_a_proplist, Options0}}}
end;
-setopts(#sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_}}}}, Options) when is_list(Options) ->
+setopts(#sslsocket{pid = {_, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, Options) when is_list(Options) ->
try ssl_socket:setopts(Transport, ListenSocket, Options) of
ok ->
ok;
@@ -419,10 +429,10 @@ shutdown(#sslsocket{pid = Pid}, How) ->
%%
%% Description: Same as inet:sockname/1
%%--------------------------------------------------------------------
-sockname(#sslsocket{pid = {Listen, #config{transport_info = {Transport,_, _, _}}}}) when is_port(Listen) ->
+sockname(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _}}}}) when is_port(Listen) ->
ssl_socket:sockname(Transport, Listen);
-sockname(#sslsocket{pid = Pid, fd = {Transport, Socket, _}}) when is_pid(Pid) ->
+sockname(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}) when is_pid(Pid) ->
ssl_socket:sockname(Transport, Socket).
%%---------------------------------------------------------------
@@ -437,8 +447,8 @@ session_info(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
{error, enotconn}.
%%---------------------------------------------------------------
--spec versions() -> [{ssl_app, string()} | {supported, [tls_atom_version()]} |
- {available, [tls_atom_version()]}].
+-spec versions() -> [{ssl_app, string()} | {supported, [tls_record:tls_atom_version()]} |
+ {available, [tls_record:tls_atom_version()]}].
%%
%% Description: Returns a list of relevant versions.
%%--------------------------------------------------------------------
@@ -541,7 +551,8 @@ do_connect(Address, Port,
{Transport, _, _, _} = CbInfo,
try Transport:connect(Address, Port, SocketOpts, Timeout) of
{ok, Socket} ->
- ssl_connection:connect(ConnetionCb, Address, Port, Socket, {SslOpts,EmOpts},
+ ssl_connection:connect(ConnetionCb, Address, Port, Socket,
+ {SslOpts, emulated_socket_options(EmOpts, #socket_options{}), undefined},
self(), CbInfo, Timeout);
{error, Reason} ->
{error, Reason}
@@ -554,57 +565,56 @@ do_connect(Address, Port,
{error, {options, {socket_options, UserOpts}}}
end.
-handle_options(Opts0, _Role) ->
+%% Handle extra ssl options given to ssl_accept
+handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
+ cacertfile = CaCertFile0} = InheritedSslOpts) ->
+ RecordCB = record_cb(Protocol),
+ CaCerts = handle_option(cacerts, Opts0, CaCerts0),
+ {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} = handle_verify_options(Opts0, CaCerts),
+ CaCertFile = case proplists:get_value(cacertfile, Opts0, CaCertFile0) of
+ undefined ->
+ CaCertDefault;
+ CAFile ->
+ CAFile
+ end,
+ NewVerifyOpts = InheritedSslOpts#ssl_options{cacerts = CaCerts,
+ cacertfile = CaCertFile,
+ verify = Verify,
+ verify_fun = VerifyFun,
+ fail_if_no_peer_cert = FailIfNoPeerCert},
+ SslOpts1 = lists:foldl(fun(Key, PropList) ->
+ proplists:delete(Key, PropList)
+ end, Opts0, [cacerts, cacertfile, verify, verify_fun, fail_if_no_peer_cert]),
+ case handle_option(versions, SslOpts1, []) of
+ [] ->
+ new_ssl_options(SslOpts1, NewVerifyOpts, RecordCB);
+ Value ->
+ Versions = [RecordCB:protocol_version(Vsn) || Vsn <- Value],
+ new_ssl_options(proplists:delete(versions, SslOpts1),
+ NewVerifyOpts#ssl_options{versions = Versions}, record_cb(Protocol))
+ end.
+
+%% Handle all options in listen and connect
+handle_options(Opts0) ->
Opts = proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Opts0),
- ReuseSessionFun = fun(_, _, _, _) -> true end,
+ assert_proplist(Opts),
+ RecordCb = record_cb(Opts),
- DefaultVerifyNoneFun =
- {fun(_,{bad_cert, _}, UserState) ->
- {valid, UserState};
- (_,{extension, _}, UserState) ->
- {unknown, UserState};
- (_, valid, UserState) ->
- {valid, UserState};
- (_, valid_peer, UserState) ->
- {valid, UserState}
- end, []},
-
- VerifyNoneFun = handle_option(verify_fun, Opts, DefaultVerifyNoneFun),
-
- UserFailIfNoPeerCert = handle_option(fail_if_no_peer_cert, Opts, false),
- UserVerifyFun = handle_option(verify_fun, Opts, undefined),
+ ReuseSessionFun = fun(_, _, _, _) -> true end,
CaCerts = handle_option(cacerts, Opts, undefined),
- {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} =
- %% Handle 0, 1, 2 for backwards compatibility
- case proplists:get_value(verify, Opts, verify_none) of
- 0 ->
- {verify_none, false,
- ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
- 1 ->
- {verify_peer, false,
- ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
- 2 ->
- {verify_peer, true,
- ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
- verify_none ->
- {verify_none, false,
- ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
- verify_peer ->
- {verify_peer, UserFailIfNoPeerCert,
- ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
- Value ->
- throw({error, {options, {verify, Value}}})
- end,
-
+ {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} = handle_verify_options(Opts, CaCerts),
+
CertFile = handle_option(certfile, Opts, <<>>),
-
+
+ RecordCb = record_cb(Opts),
+
Versions = case handle_option(versions, Opts, []) of
[] ->
- tls_record:supported_protocol_versions();
+ RecordCb:supported_protocol_versions();
Vsns ->
- [tls_record:protocol_version(Vsn) || Vsn <- Vsns]
+ [RecordCb:protocol_version(Vsn) || Vsn <- Vsns]
end,
SSLOptions = #ssl_options{
@@ -626,7 +636,8 @@ handle_options(Opts0, _Role) ->
user_lookup_fun = handle_option(user_lookup_fun, Opts, undefined),
psk_identity = handle_option(psk_identity, Opts, undefined),
srp_identity = handle_option(srp_identity, Opts, undefined),
- ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []), hd(Versions)),
+ ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []),
+ RecordCb:highest_protocol_version(Versions)),
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
@@ -641,7 +652,8 @@ handle_options(Opts0, _Role) ->
handle_option(client_preferred_next_protocols, Opts, undefined)),
log_alert = handle_option(log_alert, Opts, true),
server_name_indication = handle_option(server_name_indication, Opts, undefined),
- honor_cipher_order = handle_option(honor_cipher_order, Opts, false)
+ honor_cipher_order = handle_option(honor_cipher_order, Opts, false),
+ protocol = proplists:get_value(protocol, Opts, tls)
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
@@ -660,10 +672,10 @@ handle_options(Opts0, _Role) ->
proplists:delete(Key, PropList)
end, Opts, SslOptions),
- {SSLsock, Emulated} = emulated_options(SockOpts),
+ {Sock, Emulated} = emulated_options(SockOpts),
ConnetionCb = connection_cb(Opts),
- {ok, #config{ssl = SSLOptions, emulated = Emulated, inet_ssl = SSLsock,
+ {ok, #config{ssl = SSLOptions, emulated = Emulated, inet_ssl = Sock,
inet_user = SockOpts, transport_info = CbInfo, connection_cb = ConnetionCb
}}.
@@ -889,40 +901,24 @@ ca_cert_default(verify_peer, {Fun,_}, _) when is_function(Fun) ->
%% some trusted certs.
ca_cert_default(verify_peer, undefined, _) ->
"".
-
-emulated_options() ->
- [mode, packet, active, header, packet_size].
-
-internal_inet_values() ->
- [{packet_size,0},{packet, 0},{header, 0},{active, false},{mode,binary}].
-
-socket_options(InetValues) ->
- #socket_options{
- mode = proplists:get_value(mode, InetValues, lists),
- header = proplists:get_value(header, InetValues, 0),
- active = proplists:get_value(active, InetValues, active),
- packet = proplists:get_value(packet, InetValues, 0),
- packet_size = proplists:get_value(packet_size, InetValues)
- }.
-
emulated_options(Opts) ->
- emulated_options(Opts, internal_inet_values(), #socket_options{}).
-
-emulated_options([{mode,Opt}|Opts], Inet, Emulated) ->
- validate_inet_option(mode,Opt),
- emulated_options(Opts, Inet, Emulated#socket_options{mode=Opt});
-emulated_options([{header,Opt}|Opts], Inet, Emulated) ->
- validate_inet_option(header,Opt),
- emulated_options(Opts, Inet, Emulated#socket_options{header=Opt});
-emulated_options([{active,Opt}|Opts], Inet, Emulated) ->
- validate_inet_option(active,Opt),
- emulated_options(Opts, Inet, Emulated#socket_options{active=Opt});
-emulated_options([{packet,Opt}|Opts], Inet, Emulated) ->
- validate_inet_option(packet,Opt),
- emulated_options(Opts, Inet, Emulated#socket_options{packet=Opt});
-emulated_options([{packet_size,Opt}|Opts], Inet, Emulated) ->
- validate_inet_option(packet_size,Opt),
- emulated_options(Opts, Inet, Emulated#socket_options{packet_size=Opt});
+ emulated_options(Opts, ssl_socket:internal_inet_values(), ssl_socket:default_inet_values()).
+
+emulated_options([{mode, Value} = Opt |Opts], Inet, Emulated) ->
+ validate_inet_option(mode, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(mode, Emulated)]);
+emulated_options([{header, Value} = Opt | Opts], Inet, Emulated) ->
+ validate_inet_option(header, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(header, Emulated)]);
+emulated_options([{active, Value} = Opt |Opts], Inet, Emulated) ->
+ validate_inet_option(active, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(active, Emulated)]);
+emulated_options([{packet, Value} = Opt |Opts], Inet, Emulated) ->
+ validate_inet_option(packet, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(packet, Emulated)]);
+emulated_options([{packet_size, Value} = Opt | Opts], Inet, Emulated) ->
+ validate_inet_option(packet_size, Value),
+ emulated_options(Opts, Inet, [Opt | proplists:delete(packet_size, Emulated)]);
emulated_options([Opt|Opts], Inet, Emulated) ->
emulated_options(Opts, [Opt|Inet], Emulated);
emulated_options([], Inet,Emulated) ->
@@ -938,8 +934,11 @@ handle_cipher_option(Value, Version) when is_list(Value) ->
error:_->
throw({error, {options, {ciphers, Value}}})
end.
-binary_cipher_suites(Version, []) -> %% Defaults to all supported suits
- ssl_cipher:suites(Version);
+
+binary_cipher_suites(Version, []) ->
+ %% Defaults to all supported suites that does
+ %% not require explicit configuration
+ ssl_cipher:filter_suites(ssl_cipher:suites(Version));
binary_cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility
Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher, Hash, _} <- Ciphers0],
binary_cipher_suites(Version, Ciphers);
@@ -948,14 +947,15 @@ binary_cipher_suites(Version, [{_,_,_}| _] = Ciphers0) ->
binary_cipher_suites(Version, Ciphers);
binary_cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
- Supported0 = ssl_cipher:suites(Version)
+ All = ssl_cipher:suites(Version)
++ ssl_cipher:anonymous_suites()
++ ssl_cipher:psk_suites(Version)
++ ssl_cipher:srp_suites(),
- Supported = ssl_cipher:filter_suites(Supported0),
- case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of
+ case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, All)] of
[] ->
- Supported; %% Defaults to all supported suits
+ %% Defaults to all supported suites that does
+ %% not require explicit configuration
+ ssl_cipher:filter_suites(ssl_cipher:suites(Version));
Ciphers ->
Ciphers
end;
@@ -1034,6 +1034,13 @@ connection_cb(dtls) ->
connection_cb(Opts) ->
connection_cb(proplists:get_value(protocol, Opts, tls)).
+record_cb(tls) ->
+ tls_record;
+record_cb(dtls) ->
+ dtls_record;
+record_cb(Opts) ->
+ record_cb(proplists:get_value(protocol, Opts, tls)).
+
connection_sup(tls_connection) ->
tls_connection_sup;
connection_sup(dtls_connection) ->
@@ -1042,3 +1049,124 @@ connection_sup(dtls_connection) ->
binary_filename(FileName) ->
Enc = file:native_name_encoding(),
unicode:characters_to_binary(FileName, unicode, Enc).
+
+assert_proplist([]) ->
+ true;
+assert_proplist([{Key,_} | Rest]) when is_atom(Key) ->
+ assert_proplist(Rest);
+%% Handle exceptions
+assert_proplist([inet | Rest]) ->
+ assert_proplist(Rest);
+assert_proplist([inet6 | Rest]) ->
+ assert_proplist(Rest);
+assert_proplist([Value | _]) ->
+ throw({option_not_a_key_value_tuple, Value}).
+
+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) ->
+ new_ssl_options(Rest, Opts#ssl_options{verify_client_once = validate_option(verify_client_once, Value)}, RecordCB);
+new_ssl_options([{depth, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{depth = validate_option(depth, Value)}, RecordCB);
+new_ssl_options([{cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{cert = validate_option(cert, Value)}, RecordCB);
+new_ssl_options([{certfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{certfile = validate_option(certfile, Value)}, RecordCB);
+new_ssl_options([{key, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{key = validate_option(key, Value)}, RecordCB);
+new_ssl_options([{keyfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{keyfile = validate_option(keyfile, Value)}, RecordCB);
+new_ssl_options([{password, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{password = validate_option(password, Value)}, RecordCB);
+new_ssl_options([{dh, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{dh = validate_option(dh, Value)}, RecordCB);
+new_ssl_options([{dhfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{dhfile = validate_option(dhfile, Value)}, RecordCB);
+new_ssl_options([{user_lookup_fun, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{user_lookup_fun = validate_option(user_lookup_fun, Value)}, RecordCB);
+new_ssl_options([{psk_identity, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{psk_identity = validate_option(psk_identity, Value)}, RecordCB);
+new_ssl_options([{srp_identity, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{srp_identity = validate_option(srp_identity, Value)}, RecordCB);
+new_ssl_options([{ciphers, Value} | Rest], #ssl_options{versions = Versions} = Opts, RecordCB) ->
+ Ciphers = handle_cipher_option(Value, RecordCB:highest_protocol_version(Versions)),
+ new_ssl_options(Rest,
+ Opts#ssl_options{ciphers = Ciphers}, RecordCB);
+new_ssl_options([{reuse_session, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{reuse_session = validate_option(reuse_session, Value)}, RecordCB);
+new_ssl_options([{reuse_sessions, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{reuse_sessions = validate_option(reuse_sessions, Value)}, RecordCB);
+new_ssl_options([{ssl_imp, _Value} | Rest], #ssl_options{} = Opts, RecordCB) -> %% Not used backwards compatibility
+ new_ssl_options(Rest, Opts, RecordCB);
+new_ssl_options([{renegotiate_at, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{ renegotiate_at = validate_option(renegotiate_at, Value)}, RecordCB);
+new_ssl_options([{secure_renegotiate, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{secure_renegotiate = validate_option(secure_renegotiate, Value)}, RecordCB);
+new_ssl_options([{hibernate_after, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{hibernate_after = validate_option(hibernate_after, Value)}, RecordCB);
+new_ssl_options([{next_protocols_advertised, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{next_protocols_advertised = validate_option(next_protocols_advertised, Value)}, RecordCB);
+new_ssl_options([{client_preferred_next_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{next_protocol_selector =
+ make_next_protocol_selector(validate_option(client_preferred_next_protocols, Value))}, RecordCB);
+new_ssl_options([{log_alert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{log_alert = validate_option(log_alert, Value)}, RecordCB);
+new_ssl_options([{server_name_indication, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ 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([{Key, Value} | _Rest], #ssl_options{}, _) ->
+ throw({error, {options, {Key, Value}}}).
+
+
+handle_verify_options(Opts, CaCerts) ->
+ DefaultVerifyNoneFun =
+ {fun(_,{bad_cert, _}, UserState) ->
+ {valid, UserState};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []},
+ VerifyNoneFun = handle_option(verify_fun, Opts, DefaultVerifyNoneFun),
+
+ UserFailIfNoPeerCert = handle_option(fail_if_no_peer_cert, Opts, false),
+ UserVerifyFun = handle_option(verify_fun, Opts, undefined),
+
+
+ %% Handle 0, 1, 2 for backwards compatibility
+ case proplists:get_value(verify, Opts, verify_none) of
+ 0 ->
+ {verify_none, false,
+ ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
+ 1 ->
+ {verify_peer, false,
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
+ 2 ->
+ {verify_peer, true,
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
+ verify_none ->
+ {verify_none, false,
+ ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
+ verify_peer ->
+ {verify_peer, UserFailIfNoPeerCert,
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
+ Value ->
+ throw({error, {options, {verify, Value}}})
+ end.
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index 5c842b4d19..78dc98bc25 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,22 +31,31 @@
-include("ssl_record.hrl").
-include("ssl_internal.hrl").
--export([encode/3, alert_txt/1, reason_code/2]).
+-export([encode/3, decode/1, alert_txt/1, reason_code/2]).
%%====================================================================
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
--spec encode(#alert{}, tls_version(), #connection_states{}) ->
+-spec encode(#alert{}, ssl_record:ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
-%% Description:
+%% 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
+%% sends garbage or too many warning alerts.
+%%--------------------------------------------------------------------
+decode(Bin) ->
+ decode(Bin, [], 0).
+
+%%--------------------------------------------------------------------
-spec reason_code(#alert{}, client | server) -> closed | {essl, string()}.
%%
%% Description: Returns the error reason that will be returned to the
@@ -71,6 +80,22 @@ alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}})
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+
+%% It is very unlikely that an correct implementation will send more than one alert at the time
+%% So it there is more than 10 warning alerts we consider it an error
+decode(<<?BYTE(Level), ?BYTE(_), _/binary>>, _, N) when Level == ?WARNING, N > ?MAX_ALERTS ->
+ ?ALERT_REC(?FATAL, ?DECODE_ERROR);
+decode(<<?BYTE(Level), ?BYTE(Description), Rest/binary>>, Acc, N) when Level == ?WARNING ->
+ Alert = ?ALERT_REC(Level, Description),
+ decode(Rest, [Alert | Acc], N + 1);
+decode(<<?BYTE(Level), ?BYTE(Description), _Rest/binary>>, Acc, _) when Level == ?FATAL->
+ Alert = ?ALERT_REC(Level, Description),
+ lists:reverse([Alert | Acc]); %% No need to decode rest fatal alert will end the connection
+decode(<<?BYTE(_Level), _/binary>>, _, _) ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+decode(<<>>, Acc, _) ->
+ lists:reverse(Acc, []).
+
level_txt(?WARNING) ->
"Warning:";
level_txt(?FATAL) ->
@@ -124,5 +149,17 @@ description_txt(?USER_CANCELED) ->
"user canceled";
description_txt(?NO_RENEGOTIATION) ->
"no renegotiation";
+description_txt(?UNSUPPORTED_EXTENSION) ->
+ "unsupported extension";
+description_txt(?CERTIFICATE_UNOBTAINABLE) ->
+ "certificate unobtainable";
+description_txt(?UNRECOGNISED_NAME) ->
+ "unrecognised name";
+description_txt(?BAD_CERTIFICATE_STATUS_RESPONSE) ->
+ "bad certificate status response";
+description_txt(?BAD_CERTIFICATE_HASH_VALUE) ->
+ "bad certificate hash value";
description_txt(?UNKNOWN_PSK_IDENTITY) ->
- "unknown psk identity".
+ "unknown psk identity";
+description_txt(Enum) ->
+ lists:flatten(io_lib:format("unsupported/unknown alert: ~p", [Enum])).
diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl
index 2a8a91aefa..f4f1d74264 100644
--- a/lib/ssl/src/ssl_alert.hrl
+++ b/lib/ssl/src/ssl_alert.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -48,7 +48,7 @@
%% unsupported_certificate(43),
%% certificate_revoked(44),
%% certificate_expired(45),
- %% certificate_unknown(46),
+%% certificate_unknown(46),
%% illegal_parameter(47),
%% unknown_ca(48),
%% access_denied(49),
@@ -60,6 +60,13 @@
%% internal_error(80),
%% user_canceled(90),
%% no_renegotiation(100),
+%% RFC 4366
+%% unsupported_extension(110),
+%% certificate_unobtainable(111),
+%% unrecognized_name(112),
+%% bad_certificate_status_response(113),
+%% bad_certificate_hash_value(114),
+%% RFC 4366
%% unknown_psk_identity(115),
%% (255)
%% } AlertDescription;
@@ -88,10 +95,17 @@
-define(INTERNAL_ERROR, 80).
-define(USER_CANCELED, 90).
-define(NO_RENEGOTIATION, 100).
+-define(UNSUPPORTED_EXTENSION, 110).
+-define(CERTIFICATE_UNOBTAINABLE, 111).
+-define(UNRECOGNISED_NAME, 112).
+-define(BAD_CERTIFICATE_STATUS_RESPONSE, 113).
+-define(BAD_CERTIFICATE_HASH_VALUE, 114).
-define(UNKNOWN_PSK_IDENTITY, 115).
-define(ALERT_REC(Level,Desc), #alert{level=Level,description=Desc,where={?FILE, ?LINE}}).
+-define(MAX_ALERTS, 10).
+
%% Alert
-record(alert, {
level,
diff --git a/lib/ssl/src/ssl_api.hrl b/lib/ssl/src/ssl_api.hrl
index 607991750f..22185ff60a 100644
--- a/lib/ssl/src/ssl_api.hrl
+++ b/lib/ssl/src/ssl_api.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,8 +24,6 @@
%% Visible in API
-export_type([connect_option/0, listen_option/0, ssl_option/0, transport_option/0,
- erl_cipher_suite/0, %% From ssl_cipher.hrl
- tls_atom_version/0, %% From ssl_internal.hrl
prf_random/0, sslsocket/0]).
@@ -39,23 +37,24 @@
-type listen_option() :: socket_listen_option() | ssl_option() | transport_option().
-type socket_listen_option() :: gen_tcp:listen_option().
--type ssl_option() :: {verify, verify_type()} |
- {verify_fun, {fun(), InitialUserState::term()}} |
- {fail_if_no_peer_cert, boolean()} | {depth, integer()} |
- {cert, Der::binary()} | {certfile, path()} | {key, Der::binary()} |
- {keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} |
- {cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} |
- {user_lookup_fun, {fun(), InitialUserState::term()}} |
- {psk_identity, string()} |
- {srp_identity, {string(), string()}} |
- {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} |
- {reuse_session, fun()} | {hibernate_after, integer()|undefined} |
- {next_protocols_advertised, list(binary())} |
- {client_preferred_next_protocols, binary(), client | server, list(binary())}.
+-type ssl_option() :: {versions, ssl_record:ssl_atom_version()} |
+ {verify, verify_type()} |
+ {verify_fun, {fun(), InitialUserState::term()}} |
+ {fail_if_no_peer_cert, boolean()} | {depth, integer()} |
+ {cert, Der::binary()} | {certfile, path()} | {key, Der::binary()} |
+ {keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} |
+ {cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} |
+ {user_lookup_fun, {fun(), InitialUserState::term()}} |
+ {psk_identity, string()} |
+ {srp_identity, {string(), string()}} |
+ {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} |
+ {reuse_session, fun()} | {hibernate_after, integer()|undefined} |
+ {next_protocols_advertised, list(binary())} |
+ {client_preferred_next_protocols, binary(), client | server, list(binary())}.
-type verify_type() :: verify_none | verify_peer.
-type path() :: string().
--type ciphers() :: [erl_cipher_suite()] |
+-type ciphers() :: [ssl_cipher:erl_cipher_suite()] |
string(). % (according to old API)
-type ssl_imp() :: new | old.
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index b2077c662a..72467ea2a0 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,10 +34,26 @@
-export([security_parameters/2, security_parameters/3, suite_definition/1,
decipher/5, cipher/5,
- suite/1, suites/1, ec_keyed_suites/0, anonymous_suites/0, psk_suites/1, srp_suites/0,
+ suite/1, suites/1, all_suites/1,
+ ec_keyed_suites/0, anonymous_suites/0, psk_suites/1, srp_suites/0,
openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2]).
+-export_type([cipher_suite/0,
+ erl_cipher_suite/0, openssl_cipher_suite/0,
+ key_algo/0]).
+
+-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc'
+ | aes_128_cbc | aes_256_cbc.
+-type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512.
+-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
+-type erl_cipher_suite() :: {key_algo(), cipher(), hash()}.
+-type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash() | default_prf}.
+-type cipher_suite() :: binary().
+-type cipher_enum() :: integer().
+-type openssl_cipher_suite() :: string().
+
+
-compile(inline).
%%--------------------------------------------------------------------
@@ -51,7 +67,7 @@ security_parameters(?TLS_NULL_WITH_NULL_NULL = CipherSuite, SecParams) ->
security_parameters(undefined, CipherSuite, SecParams).
%%--------------------------------------------------------------------
--spec security_parameters(tls_version() | undefined, cipher_suite(), #security_parameters{}) ->
+-spec security_parameters(ssl_record:ssl_version() | undefined, cipher_suite(), #security_parameters{}) ->
#security_parameters{}.
%%
%% Description: Returns a security parameters record where the
@@ -72,7 +88,7 @@ security_parameters(Version, CipherSuite, SecParams) ->
hash_size = hash_size(Hash)}.
%%--------------------------------------------------------------------
--spec cipher(cipher_enum(), #cipher_state{}, binary(), iolist(), tls_version()) ->
+-spec cipher(cipher_enum(), #cipher_state{}, binary(), iodata(), ssl_record:ssl_version()) ->
{binary(), #cipher_state{}}.
%%
%% Description: Encrypts the data and the MAC using chipher described
@@ -127,7 +143,7 @@ block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
{T, CS0#cipher_state{iv=NextIV}}.
%%--------------------------------------------------------------------
--spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), tls_version()) ->
+-spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), ssl_record:ssl_version()) ->
{binary(), binary(), #cipher_state{}} | #alert{}.
%%
%% Description: Decrypts the data and the MAC using cipher described
@@ -200,7 +216,7 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
end.
%%--------------------------------------------------------------------
--spec suites(tls_version()) -> [cipher_suite()].
+-spec suites(ssl_record:ssl_version()) -> [cipher_suite()].
%%
%% Description: Returns a list of supported cipher suites.
%%--------------------------------------------------------------------
@@ -209,6 +225,11 @@ suites({3, 0}) ->
suites({3, N}) ->
tls_v1:suites(N).
+all_suites(Version) ->
+ suites(Version)
+ ++ ssl_cipher:anonymous_suites()
+ ++ ssl_cipher:psk_suites(Version)
+ ++ ssl_cipher:srp_suites().
%%--------------------------------------------------------------------
-spec anonymous_suites() -> [cipher_suite()].
%%
@@ -229,7 +250,7 @@ anonymous_suites() ->
?TLS_ECDH_anon_WITH_AES_256_CBC_SHA].
%%--------------------------------------------------------------------
--spec psk_suites(tls_version() | integer()) -> [cipher_suite()].
+-spec psk_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
%%
%% Description: Returns a list of the PSK cipher suites, only supported
%% if explicitly set by user.
@@ -998,7 +1019,8 @@ openssl_suite_name(Cipher) ->
%%--------------------------------------------------------------------
-spec filter(undefined | binary(), [cipher_suite()]) -> [cipher_suite()].
%%
-%% Description: .
+%% Description: Select the cipher suites that can be used together with the
+%% supplied certificate. (Server side functionality)
%%-------------------------------------------------------------------
filter(undefined, Ciphers) ->
Ciphers;
@@ -1032,7 +1054,7 @@ filter(DerCert, Ciphers) ->
%%--------------------------------------------------------------------
-spec filter_suites([cipher_suite()]) -> [cipher_suite()].
%%
-%% Description: filter suites for algorithms
+%% Description: Filter suites for algorithms supported by crypto.
%%-------------------------------------------------------------------
filter_suites(Suites = [{_,_,_}|_]) ->
Algos = crypto:supports(),
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
index 62a5269def..3ce9c19aa9 100644
--- a/lib/ssl/src/ssl_cipher.hrl
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,16 +26,6 @@
-ifndef(ssl_cipher).
-define(ssl_cipher, true).
--type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc'
- | aes_128_cbc | aes_256_cbc.
--type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512.
--type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
--type erl_cipher_suite() :: {key_algo(), cipher(), hash()}.
--type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash() | default_prf}.
--type cipher_suite() :: binary().
--type cipher_enum() :: integer().
--type openssl_cipher_suite() :: string().
-
%%% SSL cipher protocol %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-define(CHANGE_CIPHER_SPEC_PROTO, 1). % _PROTO to not clash with
% SSL record protocol
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index e283e6079e..34006612a2 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -36,8 +36,8 @@
-include_lib("public_key/include/public_key.hrl").
%% Setup
--export([connect/8, ssl_accept/7, handshake/2,
- socket_control/4]).
+-export([connect/8, ssl_accept/7, handshake/2, handshake/3,
+ socket_control/4, socket_control/5]).
%% User Events
-export([send/2, recv/3, close/1, shutdown/2,
@@ -50,7 +50,8 @@
%% SSL FSM state functions
-export([hello/3, abbreviated/3, certify/3, cipher/3, connection/3]).
%% SSL all state functions
--export([handle_sync_event/4, handle_info/3, terminate/3]).
+-export([handle_sync_event/4, handle_info/3, terminate/3, format_status/2]).
+
%%====================================================================
%% Internal application API
@@ -99,6 +100,20 @@ handshake(#sslsocket{pid = Pid}, Timeout) ->
Error ->
Error
end.
+
+%%--------------------------------------------------------------------
+-spec handshake(#sslsocket{}, #ssl_options{}, timeout()) -> ok | {error, reason()}.
+%%
+%% Description: Starts ssl handshake with some new options
+%%--------------------------------------------------------------------
+handshake(#sslsocket{pid = Pid}, SslOptions, Timeout) ->
+ case sync_send_all_state_event(Pid, {start, SslOptions, Timeout}) of
+ connected ->
+ ok;
+ Error ->
+ Error
+ end.
+
%--------------------------------------------------------------------
-spec socket_control(tls_connection | dtls_connection, port(), pid(), atom()) ->
{ok, #sslsocket{}} | {error, reason()}.
@@ -106,9 +121,16 @@ handshake(#sslsocket{pid = Pid}, Timeout) ->
%% Description: Set the ssl process to own the accept socket
%%--------------------------------------------------------------------
socket_control(Connection, Socket, Pid, Transport) ->
+ socket_control(Connection, Socket, Pid, Transport, undefined).
+
+%--------------------------------------------------------------------
+-spec socket_control(tls_connection | dtls_connection, port(), pid(), atom(), pid()| undefined) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+%%--------------------------------------------------------------------
+socket_control(Connection, Socket, Pid, Transport, ListenTracker) ->
case Transport:controlling_process(Socket, Pid) of
ok ->
- {ok, ssl_socket:socket(Pid, Transport, Socket, Connection)};
+ {ok, ssl_socket:socket(Pid, Transport, Socket, Connection, ListenTracker)};
{error, Reason} ->
{error, Reason}
end.
@@ -275,12 +297,11 @@ hello(#hello_request{}, #state{role = client} = State0, Connection) ->
{Record, State} = Connection:next_record(State0),
Connection:next_state(hello, hello, Record, State);
-hello({common_client_hello, Type, ServerHelloExt, HashSign},
- #state{session = #session{cipher_suite = CipherSuite},
- negotiated_version = Version} = State, Connection) ->
- {KeyAlg, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
- NegotiatedHashSign = negotiated_hashsign(HashSign, KeyAlg, Version),
+hello({common_client_hello, Type, ServerHelloExt, NegotiatedHashSign},
+ State, Connection) ->
do_server_hello(Type, ServerHelloExt,
+ %% Note NegotiatedHashSign is only negotiated for real if
+ %% if TLS version is at least TLS-1.2
State#state{hashsign_algorithm = NegotiatedHashSign}, Connection);
hello(timeout, State, _) ->
@@ -417,7 +438,8 @@ certify(#server_key_exchange{exchange_keys = Keys},
calculate_secret(Params#server_key_params.params,
State#state{hashsign_algorithm = HashSign}, Connection);
false ->
- ?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
+ Connection:handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR),
+ Version, certify, State)
end
end;
@@ -426,8 +448,9 @@ certify(#server_key_exchange{} = Msg,
Connection:handle_unexpected_message(Msg, certify_server_keyexchange, State);
certify(#certificate_request{hashsign_algorithms = HashSigns},
- #state{session = #session{own_certificate = Cert}} = State0, Connection) ->
- HashSign = ssl_handshake:select_hashsign(HashSigns, Cert),
+ #state{session = #session{own_certificate = Cert},
+ negotiated_version = Version} = State0, Connection) ->
+ HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version),
{Record, State} = Connection:next_record(State0#state{client_certificate_requested = true}),
Connection:next_state(certify, certify, Record,
State#state{cert_hashsign_algorithm = HashSign});
@@ -544,7 +567,7 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS
tls_handshake_history = Handshake
} = State0, Connection) ->
- HashSign = ssl_handshake:select_cert_hashsign(CertHashSign, Algo, Version),
+ HashSign = ssl_handshake:select_hashsign_algs(CertHashSign, Algo, Version),
case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
Version, HashSign, MasterSecret, Handshake) of
valid ->
@@ -586,7 +609,7 @@ cipher(#finished{verify_data = Data} = Finished,
cipher(#next_protocol{selected_protocol = SelectedProtocol},
#state{role = server, expecting_next_protocol_negotiation = true} = State0, Connection) ->
{Record, State} = Connection:next_record(State0#state{next_protocol = SelectedProtocol}),
- Connection:next_state(cipher, cipher, Record, State);
+ Connection:next_state(cipher, cipher, Record, State#state{expecting_next_protocol_negotiation = false});
cipher(timeout, State, _) ->
{next_state, cipher, State, hibernate};
@@ -626,12 +649,27 @@ handle_sync_event({application_data, Data}, From, StateName,
State#state{send_queue = queue:in({From, Data}, Queue)},
get_timeout(State)};
-handle_sync_event({start, Timeout}, StartFrom, hello, #state{protocol_cb = Connection} = State) ->
- Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
- Connection:hello(start, State#state{start_or_recv_from = StartFrom,
- timer = Timer});
+handle_sync_event({start, Timeout}, StartFrom, hello, #state{role = Role,
+ protocol_cb = Connection,
+ ssl_options = SSLOpts} = State0) ->
+ try
+ State = ssl_config(SSLOpts, Role, State0),
+ Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
+ Connection:hello(start, State#state{start_or_recv_from = StartFrom,
+ timer = Timer})
+ catch throw:Error ->
+ {stop, normal, {error, Error}, State0}
+ end;
-%% The two clauses below could happen if a server upgrades a socket in
+handle_sync_event({start, {Opts, EmOpts}, Timeout}, From, StateName, State) ->
+ try
+ handle_sync_event({start, Timeout}, From, StateName, State#state{socket_options = EmOpts,
+ ssl_options = Opts})
+ catch throw:Error ->
+ {stop, normal, {error, Error}, State}
+ end;
+
+%% These two clauses below could happen if a server upgrades a socket in
%% active mode. Note that in this case we are lucky that
%% controlling_process has been evalueated before receiving handshake
%% messages from client. The server should put the socket in passive
@@ -641,13 +679,16 @@ handle_sync_event({start, Timeout}, StartFrom, hello, #state{protocol_cb = Conne
%% they upgrade an active socket.
handle_sync_event({start,_}, _, connection, State) ->
{reply, connected, connection, State, get_timeout(State)};
-handle_sync_event({start,_}, _From, error, {Error, State = #state{}}) ->
- {stop, {shutdown, Error}, {error, Error}, State};
-handle_sync_event({start, Timeout}, StartFrom, StateName, State) ->
- Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
- {next_state, StateName, State#state{start_or_recv_from = StartFrom,
- timer = Timer}, get_timeout(State)};
+handle_sync_event({start, Timeout}, StartFrom, StateName, #state{role = Role, ssl_options = SslOpts} = State0) ->
+ try
+ State = ssl_config(SslOpts, Role, State0),
+ Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
+ {next_state, StateName, State#state{start_or_recv_from = StartFrom,
+ timer = Timer}, get_timeout(State)}
+ catch throw:Error ->
+ {stop, normal, {error, Error}, State0}
+ end;
handle_sync_event(close, _, StateName, #state{protocol_cb = Connection} = State) ->
%% Run terminate before returning
@@ -655,7 +696,6 @@ handle_sync_event(close, _, StateName, #state{protocol_cb = Connection} = State)
%% as intended.
(catch Connection:terminate(user_close, StateName, State)),
{stop, normal, ok, State#state{terminated = true}};
-
handle_sync_event({shutdown, How0}, _, StateName,
#state{transport_cb = Transport,
negotiated_version = Version,
@@ -677,13 +717,14 @@ handle_sync_event({shutdown, How0}, _, StateName,
Error ->
{stop, normal, Error, State}
end;
-
+handle_sync_event({recv, _N, _Timeout}, _RecvFrom, StateName,
+ #state{socket_options = #socket_options{active = Active}} = State) when Active =/= false ->
+ {reply, {error, einval}, StateName, State, get_timeout(State)};
handle_sync_event({recv, N, Timeout}, RecvFrom, connection = StateName,
#state{protocol_cb = Connection} = State0) ->
Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
Connection:passive_receive(State0#state{bytes_to_read = N,
start_or_recv_from = RecvFrom, timer = Timer}, StateName);
-
%% Doing renegotiate wait with handling request until renegotiate is
%% finished. Will be handled by next_state_is_connection/2.
handle_sync_event({recv, N, Timeout}, RecvFrom, StateName, State) ->
@@ -691,26 +732,22 @@ handle_sync_event({recv, N, Timeout}, RecvFrom, StateName, State) ->
{next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom,
timer = Timer},
get_timeout(State)};
-
handle_sync_event({new_user, User}, _From, StateName,
State =#state{user_application = {OldMon, _}}) ->
NewMon = erlang:monitor(process, User),
erlang:demonitor(OldMon, [flush]),
{reply, ok, StateName, State#state{user_application = {NewMon,User}},
get_timeout(State)};
-
handle_sync_event({get_opts, OptTags}, _From, StateName,
#state{socket = Socket,
transport_cb = Transport,
socket_options = SockOpts} = State) ->
OptsReply = get_socket_opts(Transport, Socket, OptTags, SockOpts, []),
{reply, OptsReply, StateName, State, get_timeout(State)};
-
handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = undefined} = State) ->
{reply, {error, next_protocol_not_negotiated}, StateName, State, get_timeout(State)};
handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = NextProtocol} = State) ->
{reply, {ok, NextProtocol}, StateName, State, get_timeout(State)};
-
handle_sync_event({set_opts, Opts0}, _From, StateName0,
#state{socket_options = Opts1,
protocol_cb = Connection,
@@ -749,13 +786,10 @@ handle_sync_event({set_opts, Opts0}, _From, StateName0,
end
end
end;
-
handle_sync_event(renegotiate, From, connection, #state{protocol_cb = Connection} = State) ->
Connection:renegotiate(State#state{renegotiation = {true, From}});
-
handle_sync_event(renegotiate, _, StateName, State) ->
{reply, {error, already_renegotiating}, StateName, State, get_timeout(State)};
-
handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
#state{connection_states = ConnectionStates,
negotiated_version = Version} = State) ->
@@ -781,7 +815,6 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
error:Reason -> {error, Reason}
end,
{reply, Reply, StateName, State, get_timeout(State)};
-
handle_sync_event(info, _, StateName,
#state{negotiated_version = Version,
session = #session{cipher_suite = Suite}} = State) ->
@@ -789,14 +822,12 @@ handle_sync_event(info, _, StateName,
AtomVersion = tls_record:protocol_version(Version),
{reply, {ok, {AtomVersion, ssl:suite_definition(Suite)}},
StateName, State, get_timeout(State)};
-
handle_sync_event(session_info, _, StateName,
#state{session = #session{session_id = Id,
cipher_suite = Suite}} = State) ->
{reply, [{session_id, Id},
{cipher_suite, ssl:suite_definition(Suite)}],
StateName, State, get_timeout(State)};
-
handle_sync_event(peer_certificate, _, StateName,
#state{session = #session{peer_certificate = Cert}}
= State) ->
@@ -806,8 +837,9 @@ handle_info({ErrorTag, Socket, econnaborted}, StateName,
#state{socket = Socket, transport_cb = Transport,
start_or_recv_from = StartFrom, role = Role,
protocol_cb = Connection,
- error_tag = ErrorTag} = State) when StateName =/= connection ->
- Connection:alert_user(Transport, Socket, StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role),
+ error_tag = ErrorTag,
+ tracker = Tracker} = State) when StateName =/= connection ->
+ Connection:alert_user(Transport, Tracker,Socket, StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role),
{stop, normal, State};
handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket,
@@ -857,7 +889,6 @@ terminate(_, _, #state{terminated = true}) ->
%% we want to guarantee that Transport:close has been called
%% when ssl:close/1 returns.
ok;
-
terminate({shutdown, transport_closed}, StateName, #state{send_queue = SendQueue,
renegotiation = Renegotiate} = State) ->
handle_unrecv_data(StateName, State),
@@ -870,7 +901,6 @@ terminate({shutdown, own_alert}, _StateName, #state{send_queue = SendQueue,
handle_trusted_certs_db(State),
notify_senders(SendQueue),
notify_renegotiater(Renegotiate);
-
terminate(Reason, connection, #state{negotiated_version = Version,
protocol_cb = Connection,
connection_states = ConnectionStates,
@@ -887,7 +917,6 @@ terminate(Reason, connection, #state{negotiated_version = Version,
_ ->
ok
end;
-
terminate(_Reason, _StateName, #state{transport_cb = Transport,
socket = Socket, send_queue = SendQueue,
renegotiation = Renegotiate} = State) ->
@@ -896,9 +925,50 @@ terminate(_Reason, _StateName, #state{transport_cb = Transport,
notify_renegotiater(Renegotiate),
Transport:close(Socket).
+format_status(normal, [_, State]) ->
+ [{data, [{"StateData", State}]}];
+format_status(terminate, [_, State]) ->
+ SslOptions = (State#state.ssl_options),
+ NewOptions = SslOptions#ssl_options{password = "***",
+ cert = "***",
+ cacerts = "***",
+ key = "***",
+ dh = "***",
+ psk_identity = "***",
+ srp_identity = "***"},
+ [{data, [{"StateData", State#state{connection_states = "***",
+ protocol_buffers = "***",
+ user_data_buffer = "***",
+ tls_handshake_history = "***",
+ session = "***",
+ private_key = "***",
+ diffie_hellman_params = "***",
+ diffie_hellman_keys = "***",
+ srp_params = "***",
+ srp_keys = "***",
+ premaster_secret = "***",
+ ssl_options = NewOptions
+ }}]}].
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+ssl_config(Opts, Role, State) ->
+ {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} =
+ ssl_config:init(Opts, Role),
+ Handshake = ssl_handshake:init_handshake_history(),
+ TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ Session = State#state.session,
+ State#state{tls_handshake_history = Handshake,
+ session = Session#session{own_certificate = OwnCert,
+ time_stamp = TimeStamp},
+ file_ref_db = FileRefHandle,
+ cert_db_ref = Ref,
+ cert_db = CertDbHandle,
+ session_cache = CacheHandle,
+ private_key = Key,
+ diffie_hellman_params = DHParams,
+ ssl_options = Opts}.
+
do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocols} =
ServerHelloExt,
#state{negotiated_version = Version,
@@ -1540,60 +1610,6 @@ cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0
session = Session}, cipher, Connection),
Connection:next_state_connection(cipher, ack_connection(State#state{session = Session})).
-negotiated_hashsign(undefined, Algo, Version) ->
- default_hashsign(Version, Algo);
-negotiated_hashsign(HashSign = {_, _}, _, _) ->
- HashSign.
-
-%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
-%% If the client does not send the signature_algorithms extension, the
-%% server MUST do the following:
-%%
-%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
-%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
-%% sent the value {sha1,rsa}.
-%%
-%% - If the negotiated key exchange algorithm is one of (DHE_DSS,
-%% DH_DSS), behave as if the client had sent the value {sha1,dsa}.
-%%
-%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
-%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
-
-default_hashsign(_Version = {Major, Minor}, KeyExchange)
- when Major >= 3 andalso Minor >= 3 andalso
- (KeyExchange == rsa orelse
- KeyExchange == dhe_rsa orelse
- KeyExchange == dh_rsa orelse
- KeyExchange == ecdhe_rsa orelse
- KeyExchange == ecdh_rsa orelse
- KeyExchange == srp_rsa) ->
- {sha, rsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == rsa;
- KeyExchange == dhe_rsa;
- KeyExchange == dh_rsa;
- KeyExchange == ecdhe_rsa;
- KeyExchange == ecdh_rsa;
- KeyExchange == srp_rsa ->
- {md5sha, rsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == ecdhe_ecdsa;
- KeyExchange == ecdh_ecdsa ->
- {sha, ecdsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == dhe_dss;
- KeyExchange == dh_dss;
- KeyExchange == srp_dss ->
- {sha, dsa};
-default_hashsign(_Version, KeyExchange)
- when KeyExchange == dh_anon;
- KeyExchange == ecdh_anon;
- KeyExchange == psk;
- KeyExchange == dhe_psk;
- KeyExchange == rsa_psk;
- KeyExchange == srp_anon ->
- {null, anon}.
-
select_curve(#state{client_ecc = {[Curve|_], _}}) ->
{namedCurve, Curve};
select_curve(_) ->
@@ -1854,3 +1870,15 @@ make_premaster_secret({MajVer, MinVer}, rsa) ->
<<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>;
make_premaster_secret(_, _) ->
undefined.
+
+negotiated_hashsign(undefined, Alg, Version) ->
+ %% Not negotiated choose default
+ case is_anonymous(Alg) of
+ true ->
+ {null, anon};
+ false ->
+ ssl_handshake:select_hashsign_algs(Alg, Version)
+ end;
+negotiated_hashsign(HashSign = {_, _}, _, _) ->
+ HashSign.
+
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index 341a4217e4..592889b177 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -48,17 +48,17 @@
socket_options :: #socket_options{},
connection_states :: #connection_states{},
protocol_buffers :: term(), %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hrl
- tls_handshake_history ::tls_handshake_history(),
+ tls_handshake_history :: ssl_handshake:ssl_handshake_history(),
cert_db :: reference(),
session :: #session{},
session_cache :: db_handle(),
session_cache_cb :: atom(),
- negotiated_version :: tls_version(),
+ negotiated_version :: ssl_record:ssl_version(),
client_certificate_requested = false :: boolean(),
- key_algorithm :: key_algo(),
+ key_algorithm :: ssl_cipher:key_algo(),
hashsign_algorithm = {undefined, undefined},
cert_hashsign_algorithm,
- public_key_info ::public_key_info(),
+ public_key_info ::ssl_handshake:public_key_info(),
private_key ::public_key:private_key(),
diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side
diffie_hellman_keys, % {PublicKey, PrivateKey}
@@ -78,7 +78,8 @@
allow_renegotiate = true ::boolean(),
expecting_next_protocol_negotiation = false ::boolean(),
next_protocol = undefined :: undefined | binary(),
- client_ecc % {Curves, PointFmt}
+ client_ecc, % {Curves, PointFmt}
+ tracker :: pid() %% Tracker process for listen socket
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
diff --git a/lib/ssl/src/ssl_dist_sup.erl b/lib/ssl/src/ssl_dist_sup.erl
index 22614a2d34..58efeaf892 100644
--- a/lib/ssl/src/ssl_dist_sup.erl
+++ b/lib/ssl/src/ssl_dist_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -45,9 +45,11 @@ start_link() ->
init([]) ->
SessionCertManager = session_and_cert_manager_child_spec(),
ConnetionManager = connection_manager_child_spec(),
+ ListenOptionsTracker = listen_options_tracker_child_spec(),
ProxyServer = proxy_server_child_spec(),
- {ok, {{one_for_all, 10, 3600}, [SessionCertManager, ConnetionManager,
+ {ok, {{one_for_all, 10, 3600}, [SessionCertManager, ConnetionManager,
+ ListenOptionsTracker,
ProxyServer]}}.
%%--------------------------------------------------------------------
@@ -68,7 +70,7 @@ connection_manager_child_spec() ->
StartFunc = {tls_connection_sup, start_link_dist, []},
Restart = permanent,
Shutdown = 4000,
- Modules = [ssl_connection],
+ Modules = [tls_connection_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
@@ -81,3 +83,11 @@ proxy_server_child_spec() ->
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 245cd3e280..fc67d2c28d 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -31,6 +31,18 @@
-include("ssl_srp.hrl").
-include_lib("public_key/include/public_key.hrl").
+-export_type([ssl_handshake/0, ssl_handshake_history/0,
+ public_key_info/0, oid/0]).
+
+-type oid() :: tuple().
+-type public_key_params() :: #'Dss-Parms'{} | {namedCurve, oid()} | #'ECParameters'{} | term().
+-type public_key_info() :: {oid(), #'RSAPublicKey'{} | integer() | #'ECPoint'{}, public_key_params()}.
+-type ssl_handshake_history() :: {[binary()], [binary()]}.
+
+-type ssl_handshake() :: #server_hello{} | #server_hello_done{} | #certificate{} | #certificate_request{} |
+ #client_key_exchange{} | #finished{} | #certificate_verify{} |
+ #hello_request{} | #next_protocol{}.
+
%% Handshake messages
-export([hello_request/0, server_hello/4, server_hello_done/0,
certificate/4, certificate_request/4, key_exchange/3,
@@ -61,7 +73,8 @@
]).
%% MISC
--export([select_version/3, prf/5, select_hashsign/2, select_cert_hashsign/3,
+-export([select_version/3, prf/5, select_hashsign/3,
+ select_hashsign_algs/2, select_hashsign_algs/3,
premaster_secret/2, premaster_secret/3, premaster_secret/4]).
%%====================================================================
@@ -80,7 +93,7 @@ hello_request() ->
#hello_request{}.
%%--------------------------------------------------------------------
--spec server_hello(#session{}, tls_version(), #connection_states{},
+-spec server_hello(#session{}, ssl_record:ssl_version(), #connection_states{},
#hello_extensions{}) -> #server_hello{}.
%%
%% Description: Creates a server hello message.
@@ -164,8 +177,8 @@ next_protocol(SelectedProtocol) ->
%%--------------------------------------------------------------------
-spec client_certificate_verify(undefined | der_cert(), binary(),
- tls_version(), term(), public_key:private_key(),
- tls_handshake_history()) ->
+ ssl_record:ssl_version(), term(), public_key:private_key(),
+ ssl_handshake_history()) ->
#certificate_verify{} | ignore | #alert{}.
%%
%% Description: Creates a certificate_verify message, called by the client.
@@ -188,7 +201,7 @@ client_certificate_verify(OwnCert, MasterSecret, Version,
end.
%%--------------------------------------------------------------------
--spec certificate_request(erl_cipher_suite(), db_handle(), certdb_ref(), tls_version()) ->
+-spec certificate_request(ssl_cipher:erl_cipher_suite(), db_handle(), certdb_ref(), ssl_record:ssl_version()) ->
#certificate_request{}.
%%
%% Description: Creates a certificate_request message, called by the server.
@@ -203,7 +216,7 @@ certificate_request(CipherSuite, CertDbHandle, CertDbRef, Version) ->
certificate_authorities = Authorities
}.
%%--------------------------------------------------------------------
--spec key_exchange(client | server, tls_version(),
+-spec key_exchange(client | server, ssl_record:ssl_version(),
{premaster_secret, binary(), public_key_info()} |
{dh, binary()} |
{dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()},
@@ -304,7 +317,7 @@ key_exchange(server, Version, {srp, {PublicKey, _},
ClientRandom, ServerRandom, PrivateKey).
%%--------------------------------------------------------------------
--spec finished(tls_version(), client | server, integer(), binary(), tls_handshake_history()) ->
+-spec finished(ssl_record:ssl_version(), client | server, integer(), binary(), ssl_handshake_history()) ->
#finished{}.
%%
%% Description: Creates a handshake finished message
@@ -331,8 +344,8 @@ verify_server_key(#server_key_params{params_bin = EncParams,
verify_signature(Version, Hash, HashSign, Signature, PubKeyInfo).
%%--------------------------------------------------------------------
--spec certificate_verify(binary(), public_key_info(), tls_version(), term(),
- binary(), tls_handshake_history()) -> valid | #alert{}.
+-spec certificate_verify(binary(), public_key_info(), ssl_record:ssl_version(), term(),
+ binary(), ssl_handshake_history()) -> valid | #alert{}.
%%
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
@@ -346,7 +359,7 @@ certificate_verify(Signature, PublicKeyInfo, Version,
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
end.
%%--------------------------------------------------------------------
--spec verify_signature(tls_version(), binary(), {term(), term()}, binary(),
+-spec verify_signature(ssl_record:ssl_version(), binary(), {term(), term()}, binary(),
public_key_info()) -> true | false.
%%
%% Description: Checks that a public_key signature is valid.
@@ -426,8 +439,8 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
end.
%%--------------------------------------------------------------------
--spec verify_connection(tls_version(), #finished{}, client | server, integer(), binary(),
- tls_handshake_history()) -> verified | #alert{}.
+-spec verify_connection(ssl_record:ssl_version(), #finished{}, client | server, integer(), binary(),
+ ssl_handshake_history()) -> verified | #alert{}.
%%
%% Description: Checks the ssl handshake finished message to verify
%% the connection.
@@ -443,7 +456,7 @@ verify_connection(Version, #finished{verify_data = Data},
end.
%%--------------------------------------------------------------------
--spec init_handshake_history() -> tls_handshake_history().
+-spec init_handshake_history() -> ssl_handshake_history().
%%
%% Description: Initialize the empty handshake history buffer.
@@ -452,8 +465,8 @@ init_handshake_history() ->
{[], []}.
%%--------------------------------------------------------------------
--spec update_handshake_history(tls_handshake_history(), Data ::term()) ->
- tls_handshake_history().
+-spec update_handshake_history(ssl_handshake:ssl_handshake_history(), Data ::term()) ->
+ ssl_handshake:ssl_handshake_history().
%%
%% Description: Update the handshake history buffer with Data.
%%--------------------------------------------------------------------
@@ -567,7 +580,7 @@ server_key_exchange_hash(md5sha, Value) ->
server_key_exchange_hash(Hash, Value) ->
crypto:hash(Hash, Value).
%%--------------------------------------------------------------------
--spec prf(tls_version(), binary(), binary(), [binary()], non_neg_integer()) ->
+-spec prf(ssl_record:ssl_version(), binary(), binary(), [binary()], non_neg_integer()) ->
{ok, binary()} | {error, undefined}.
%%
%% Description: use the TLS PRF to generate key material
@@ -578,23 +591,25 @@ prf({3,1}, Secret, Label, Seed, WantedLength) ->
{ok, tls_v1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)};
prf({3,_N}, Secret, Label, Seed, WantedLength) ->
{ok, tls_v1:prf(?SHA256, Secret, Label, Seed, WantedLength)}.
+
+
%%--------------------------------------------------------------------
--spec select_hashsign(#hash_sign_algos{}| undefined, undefined | binary()) ->
- [{atom(), atom()}] | undefined.
+-spec select_hashsign(#hash_sign_algos{}| undefined, undefined | binary(), ssl_record:ssl_version()) ->
+ {atom(), atom()} | undefined.
%%
%% Description:
%%--------------------------------------------------------------------
-select_hashsign(_, undefined) ->
+select_hashsign(_, undefined, _Version) ->
{null, anon};
-select_hashsign(undefined, Cert) ->
+select_hashsign(undefined, Cert, Version) ->
#'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
#'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- select_cert_hashsign(undefined, Algo, {undefined, undefined});
-select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert) ->
+ select_hashsign_algs(undefined, Algo, Version);
+select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, Version) ->
#'OTPCertificate'{tbsCertificate = TBSCert} =public_key:pkix_decode_cert(Cert, otp),
#'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- DefaultHashSign = {_, Sign} = select_cert_hashsign(undefined, Algo, {undefined, undefined}),
+ DefaultHashSign = {_, Sign} = select_hashsign_algs(undefined, Algo, Version),
case lists:filter(fun({sha, dsa}) ->
true;
({_, dsa}) ->
@@ -610,28 +625,61 @@ select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert) ->
[HashSign| _] ->
HashSign
end.
+
%%--------------------------------------------------------------------
--spec select_cert_hashsign(#hash_sign_algos{}| undefined, oid(), tls_version() | {undefined, undefined}) ->
+-spec select_hashsign_algs(#hash_sign_algos{}| undefined, oid(), ssl_record:ssl_version()) ->
{atom(), atom()}.
+%% Description: For TLS 1.2 hash function and signature algorithm pairs can be
+%% negotiated with the signature_algorithms extension,
+%% for previous versions always use appropriate defaults.
+%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
+%% If the client does not send the signature_algorithms extension, the
+%% server MUST do the following: (e.i defaults for TLS 1.2)
+%%
+%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
+%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
+%% sent the value {sha1,rsa}.
%%
-%% Description: For TLS 1.2 selected cert_hash_sign will be recived
-%% in the handshake message, for previous versions use appropriate defaults.
-%% This function is also used by select_hashsign to extract
-%% the alogrithm of the server cert key.
+%% - If the negotiated key exchange algorithm is one of (DHE_DSS,
+%% DH_DSS), behave as if the client had sent the value {sha1,dsa}.
+%%
+%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
+%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
+
%%--------------------------------------------------------------------
-select_cert_hashsign(HashSign, _, {Major, Minor}) when HashSign =/= undefined andalso
+select_hashsign_algs(HashSign, _, {Major, Minor}) when HashSign =/= undefined andalso
Major >= 3 andalso Minor >= 3 ->
HashSign;
-select_cert_hashsign(undefined,?'id-ecPublicKey', _) ->
+select_hashsign_algs(undefined, ?rsaEncryption, {Major, Minor}) when Major >= 3 andalso Minor >= 3 ->
+ {sha, rsa};
+select_hashsign_algs(undefined,?'id-ecPublicKey', _) ->
{sha, ecdsa};
-select_cert_hashsign(undefined, ?rsaEncryption, _) ->
+select_hashsign_algs(undefined, ?rsaEncryption, _) ->
{md5sha, rsa};
-select_cert_hashsign(undefined, ?'id-dsa', _) ->
+select_hashsign_algs(undefined, ?'id-dsa', _) ->
{sha, dsa}.
+-spec select_hashsign_algs(atom(), ssl_record:ssl_version()) -> {atom(), atom()}.
+%% Wrap function to keep the knowledge of the default values in
+%% one place only
+select_hashsign_algs(Alg, Version) when (Alg == rsa orelse
+ Alg == dhe_rsa orelse
+ Alg == dh_rsa orelse
+ Alg == ecdhe_rsa orelse
+ Alg == ecdh_rsa orelse
+ Alg == srp_rsa) ->
+ select_hashsign_algs(undefined, ?rsaEncryption, Version);
+select_hashsign_algs(Alg, Version) when (Alg == dhe_dss orelse
+ Alg == dh_dss orelse
+ Alg == srp_dss) ->
+ select_hashsign_algs(undefined, ?'id-dsa', Version);
+select_hashsign_algs(Alg, Version) when (Alg == ecdhe_ecdsa orelse
+ Alg == ecdh_ecdsa) ->
+ select_hashsign_algs(undefined, ?'id-ecPublicKey', Version).
+
%%--------------------------------------------------------------------
--spec master_secret(atom(), tls_version(), #session{} | binary(), #connection_states{},
+-spec master_secret(atom(), ssl_record:ssl_version(), #session{} | binary(), #connection_states{},
client | server) -> {binary(), #connection_states{}} | #alert{}.
%%
%% Description: Sets or calculates the master secret and calculate keys,
@@ -816,7 +864,7 @@ enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo},
end.
%%--------------------------------------------------------------------
--spec decode_client_key(binary(), key_algo(), tls_version()) ->
+-spec decode_client_key(binary(), ssl_cipher:key_algo(), ssl_record:ssl_version()) ->
#encrypted_premaster_secret{}
| #client_diffie_hellman_public{}
| #client_ec_diffie_hellman_public{}
@@ -831,7 +879,7 @@ decode_client_key(ClientKey, Type, Version) ->
dec_client_key(ClientKey, key_exchange_alg(Type), Version).
%%--------------------------------------------------------------------
--spec decode_server_key(binary(), key_algo(), tls_version()) ->
+-spec decode_server_key(binary(), ssl_cipher:key_algo(), ssl_record:ssl_version()) ->
#server_key_params{}.
%%
%% Description: Decode server_key data and return appropriate type
@@ -1005,12 +1053,9 @@ decode_suites('3_bytes', Dec) ->
%%-------------Cipeher suite handling --------------------------------
available_suites(UserSuites, Version) ->
- case UserSuites of
- [] ->
- ssl_cipher:suites(Version);
- _ ->
- UserSuites
- end.
+ lists:filtermap(fun(Suite) ->
+ lists:member(Suite, ssl_cipher:all_suites(Version))
+ end, UserSuites).
available_suites(ServerCert, UserSuites, Version, Curve) ->
ssl_cipher:filter(ServerCert, available_suites(UserSuites, Version))
@@ -1654,7 +1699,16 @@ dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
dec_hello_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len),
ExtData:Len/binary, Rest/binary>>, Acc) ->
<<?UINT16(_), EllipticCurveList/binary>> = ExtData,
- EllipticCurves = [tls_v1:enum_to_oid(X) || <<X:16>> <= EllipticCurveList],
+ %% Ignore unknown curves
+ Pick = fun(Enum) ->
+ case tls_v1:enum_to_oid(Enum) of
+ undefined ->
+ false;
+ Oid ->
+ {true, Oid}
+ end
+ end,
+ EllipticCurves = lists:filtermap(Pick, [ECC || <<ECC:16>> <= EllipticCurveList]),
dec_hello_extensions(Rest, Acc#hello_extensions{elliptic_curves =
#elliptic_curves{elliptic_curve_list =
EllipticCurves}});
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index 75160526b9..80284faef0 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -352,18 +352,4 @@
hostname = undefined
}).
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Dialyzer types
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
--type oid() :: tuple().
--type public_key_params() :: #'Dss-Parms'{} | {namedCurve, oid()} | #'ECParameters'{} | term().
--type public_key_info() :: {oid(), #'RSAPublicKey'{} | integer() | #'ECPoint'{}, public_key_params()}.
--type tls_handshake_history() :: {[binary()], [binary()]}.
-
--type ssl_handshake() :: #server_hello{} | #server_hello_done{} | #certificate{} | #certificate_request{} |
- #client_key_exchange{} | #finished{} | #certificate_verify{} |
- #hello_request{} | #next_protocol{}.
-
-
-endif. % -ifdef(ssl_handshake).
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 64b89e9f95..fd0d87bd5f 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -30,8 +30,6 @@
-type from() :: term().
-type host() :: inet:ip_address() | inet:hostname().
-type session_id() :: 0 | binary().
--type tls_version() :: {integer(), integer()}.
--type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'.
-type certdb_ref() :: reference().
-type db_handle() :: term().
-type der_cert() :: binary().
@@ -73,7 +71,7 @@
-record(ssl_options, {
protocol :: tls | dtls,
- versions :: ['tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3] | ['dtlsv1.2' | dtlsv1],
+ versions :: [ssl_record:ssl_version()], %% ssl_record:atom_version() in API
verify :: verify_none | verify_peer,
verify_fun, %%:: fun(CertVerifyErrors::term()) -> boolean(),
fail_if_no_peer_cert :: boolean(),
@@ -103,7 +101,6 @@
reuse_sessions :: boolean(),
renegotiate_at,
secure_renegotiate,
- debug,
%% undefined if not hibernating, or number of ms of
%% inactivity after which ssl_connection will go into
%% hibernation
@@ -119,14 +116,6 @@
honor_cipher_order = false
}).
--record(config, {ssl, %% SSL parameters
- inet_user, %% User set inet options
- emulated, %% #socket_option{} emulated
- inet_ssl, %% inet options for internal ssl socket
- transport_info, %% Callback info
- connection_cb
- }).
-
-record(socket_options,
{
mode = list,
@@ -136,6 +125,15 @@
active = true
}).
+-record(config, {ssl, %% SSL parameters
+ inet_user, %% User set inet options
+ emulated, %% Emulated option list or "inherit_tracker" pid
+ inet_ssl, %% inet options for internal ssl socket
+ transport_info, %% Callback info
+ connection_cb
+ }).
+
+
-type state_name() :: hello | abbreviated | certify | cipher | connection.
-type gen_fsm_state_return() :: {next_state, state_name(), term()} |
{next_state, state_name(), term(), timeout()} |
diff --git a/lib/ssl/src/ssl_listen_tracker_sup.erl b/lib/ssl/src/ssl_listen_tracker_sup.erl
new file mode 100644
index 0000000000..29f40e846d
--- /dev/null
+++ b/lib/ssl/src/ssl_listen_tracker_sup.erl
@@ -0,0 +1,71 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014-2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Supervisor for a listen options tracker
+%%----------------------------------------------------------------------
+-module(ssl_listen_tracker_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0, start_link_dist/0]).
+-export([start_child/1, start_child_dist/1]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+start_link() ->
+ supervisor:start_link({local, tracker_name(normal)}, ?MODULE, []).
+
+start_link_dist() ->
+ supervisor:start_link({local, tracker_name(dist)}, ?MODULE, []).
+
+start_child(Args) ->
+ supervisor:start_child(tracker_name(normal), Args).
+
+start_child_dist(Args) ->
+ supervisor:start_child(tracker_name(dist), Args).
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+init(_O) ->
+ RestartStrategy = simple_one_for_one,
+ MaxR = 0,
+ MaxT = 3600,
+
+ Name = undefined, % As simple_one_for_one is used.
+ StartFunc = {ssl_socket, start_link, []},
+ Restart = temporary, % E.g. should not be restarted
+ Shutdown = 4000,
+ Modules = [ssl_socket],
+ Type = worker,
+
+ ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
+ {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}.
+
+tracker_name(normal) ->
+ ?MODULE;
+tracker_name(dist) ->
+ list_to_atom(atom_to_list(?MODULE) ++ "dist").
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index fbc73e0e42..2bc5a90f68 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -282,8 +282,13 @@ handle_cast({register_session, Host, Port, Session},
session_cache_cb = CacheCb} = State) ->
TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
NewSession = Session#session{time_stamp = TimeStamp},
- CacheCb:update(Cache, {{Host, Port},
- NewSession#session.session_id}, NewSession),
+ case CacheCb:select_session(Cache, {Host, Port}) of
+ no_session ->
+ CacheCb:update(Cache, {{Host, Port},
+ NewSession#session.session_id}, NewSession);
+ Sessions ->
+ register_unique_session(Sessions, NewSession, CacheCb, Cache, {Host, Port})
+ end,
{noreply, State};
handle_cast({register_session, Port, Session},
@@ -494,3 +499,34 @@ clean_cert_db(Ref, CertDb, RefDb, PemCache, File) ->
_ ->
ok
end.
+
+%% Do not let dumb clients create a gigantic session table
+register_unique_session(Sessions, Session, CacheCb, Cache, PartialKey) ->
+ case exists_equivalent(Session , Sessions) of
+ true ->
+ ok;
+ false ->
+ CacheCb:update(Cache, {PartialKey,
+ Session#session.session_id}, Session)
+ end.
+
+exists_equivalent(_, []) ->
+ false;
+exists_equivalent(#session{
+ peer_certificate = PeerCert,
+ own_certificate = OwnCert,
+ compression_method = Compress,
+ cipher_suite = CipherSuite,
+ srp_username = SRP,
+ ecc = ECC} ,
+ [#session{
+ peer_certificate = PeerCert,
+ own_certificate = OwnCert,
+ compression_method = Compress,
+ cipher_suite = CipherSuite,
+ srp_username = SRP,
+ ecc = ECC} | _]) ->
+ true;
+exists_equivalent(Session, [ _ | Rest]) ->
+ exists_equivalent(Session, Rest).
+
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 018c8befe0..7337225bc4 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -50,6 +50,11 @@
%% Payload encryption/decryption
-export([cipher/4, decipher/3, is_correct_mac/2]).
+-export_type([ssl_version/0, ssl_atom_version/0]).
+
+-type ssl_version() :: {integer(), integer()}.
+-type ssl_atom_version() :: tls_record:tls_atom_version().
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -299,7 +304,7 @@ set_pending_cipher_state(#connection_states{pending_read = Read,
%%--------------------------------------------------------------------
--spec encode_handshake(iolist(), tls_version(), #connection_states{}) ->
+-spec encode_handshake(iolist(), ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
%% Description: Encodes a handshake message to send on the ssl-socket.
@@ -308,7 +313,7 @@ encode_handshake(Frag, Version, ConnectionStates) ->
encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_alert_record(#alert{}, tls_version(), #connection_states{}) ->
+-spec encode_alert_record(#alert{}, ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
%% Description: Encodes an alert message to send on the ssl-socket.
@@ -319,7 +324,7 @@ encode_alert_record(#alert{level = Level, description = Description},
ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_change_cipher_spec(tls_version(), #connection_states{}) ->
+-spec encode_change_cipher_spec(ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
@@ -328,7 +333,7 @@ encode_change_cipher_spec(Version, ConnectionStates) ->
encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
%%--------------------------------------------------------------------
--spec encode_data(binary(), tls_version(), #connection_states{}) ->
+-spec encode_data(binary(), ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
%%
%% Description: Encodes data to send on the ssl-socket.
@@ -356,7 +361,7 @@ compressions() ->
[?byte(?NULL)].
%%--------------------------------------------------------------------
--spec cipher(tls_version(), iolist(), #connection_state{}, MacHash::binary()) ->
+-spec cipher(ssl_version(), iodata(), #connection_state{}, MacHash::binary()) ->
{CipherFragment::binary(), #connection_state{}}.
%%
%% Description: Payload encryption
@@ -372,7 +377,7 @@ cipher(Version, Fragment,
ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version),
{CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}.
%%--------------------------------------------------------------------
--spec decipher(tls_version(), binary(), #connection_state{}) -> {binary(), binary(), #connection_state{}}.
+-spec decipher(ssl_version(), binary(), #connection_state{}) -> {binary(), binary(), #connection_state{}} | #alert{}.
%%
%% Description: Payload decryption
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index c17fa53a62..6aab35d6da 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
%%
%%----------------------------------------------------------------------
%% Purpose: Record and constant defenitions for the SSL-record protocol
-%% see RFC 2246
+% see RFC 2246
%%----------------------------------------------------------------------
-ifndef(ssl_record).
@@ -70,7 +70,7 @@
-define(INITIAL_BYTES, 5).
--define(MAX_SEQENCE_NUMBER, 18446744073709552000). %% math:pow(2, 64) - 1 = 1.8446744073709552e19
+-define(MAX_SEQENCE_NUMBER, 18446744073709551615). %% (1 bsl 64) - 1 = 18446744073709551615
%% Sequence numbers can not wrap so when max is about to be reached we should renegotiate.
%% We will renegotiate a little before so that there will be sequence numbers left
%% for the rehandshake and a little data. Currently we decided to renegotiate a little more
diff --git a/lib/ssl/src/ssl_socket.erl b/lib/ssl/src/ssl_socket.erl
index 1b6e637cd3..55eb569b20 100644
--- a/lib/ssl/src/ssl_socket.erl
+++ b/lib/ssl/src/ssl_socket.erl
@@ -1,20 +1,73 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
-module(ssl_socket).
+-behaviour(gen_server).
+
-include("ssl_internal.hrl").
-include("ssl_api.hrl").
--export([socket/4, setopts/3, getopts/3, peername/2, sockname/2, port/2]).
+-export([socket/5, setopts/3, getopts/3, peername/2, sockname/2, port/2]).
+-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,
+ set_emulated_opts/2, get_all_opts/1, handle_call/3, handle_cast/2,
+ handle_info/2, code_change/3]).
+
+-record(state, {
+ emulated_opts,
+ port,
+ ssl_opts
+ }).
-socket(Pid, Transport, Socket, ConnectionCb) ->
+%%--------------------------------------------------------------------
+%%% Internal API
+%%--------------------------------------------------------------------
+socket(Pid, Transport, Socket, ConnectionCb, Tracker) ->
#sslsocket{pid = Pid,
%% "The name "fd" is keept for backwards compatibility
- fd = {Transport, Socket, ConnectionCb}}.
-
+ fd = {Transport, Socket, ConnectionCb, Tracker}}.
+setopts(gen_tcp, #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) ->
+ {SockOpts, EmulatedOpts} = split_options(Options),
+ ok = set_emulated_opts(Tracker, EmulatedOpts),
+ inet:setopts(ListenSocket, SockOpts);
+setopts(_, #sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_},
+ emulated = Tracker}}}, Options) ->
+ {SockOpts, EmulatedOpts} = split_options(Options),
+ ok = set_emulated_opts(Tracker, EmulatedOpts),
+ Transport:setopts(ListenSocket, SockOpts);
+%%% Following clauses will not be called for emulated options, they are handled in the connection process
setopts(gen_tcp, Socket, Options) ->
inet:setopts(Socket, Options);
setopts(Transport, Socket, Options) ->
Transport:setopts(Socket, Options).
+getopts(gen_tcp, #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) ->
+ {SockOptNames, EmulatedOptNames} = split_options(Options),
+ EmulatedOpts = get_emulated_opts(Tracker, EmulatedOptNames),
+ SocketOpts = get_socket_opts(ListenSocket, SockOptNames, inet),
+ {ok, EmulatedOpts ++ SocketOpts};
+getopts(Transport, #sslsocket{pid = {ListenSocket, #config{emulated = Tracker}}}, Options) ->
+ {SockOptNames, EmulatedOptNames} = split_options(Options),
+ EmulatedOpts = get_emulated_opts(Tracker, EmulatedOptNames),
+ SocketOpts = 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_tcp, Socket, Options) ->
inet:getopts(Socket, Options);
getopts(Transport, Socket, Options) ->
@@ -34,3 +87,151 @@ port(gen_tcp, Socket) ->
inet:port(Socket);
port(Transport, Socket) ->
Transport:port(Socket).
+
+emulated_options() ->
+ [mode, packet, active, header, packet_size].
+
+internal_inet_values() ->
+ [{packet_size,0}, {packet, 0}, {header, 0}, {active, false}, {mode,binary}].
+
+default_inet_values() ->
+ [{packet_size, 0}, {packet,0}, {header, 0}, {active, true}, {mode, list}].
+
+inherit_tracker(ListenSocket, EmOpts, #ssl_options{erl_dist = false} = SslOpts) ->
+ ssl_listen_tracker_sup:start_child([ListenSocket, EmOpts, SslOpts]);
+inherit_tracker(ListenSocket, EmOpts, #ssl_options{erl_dist = true} = SslOpts) ->
+ ssl_listen_tracker_sup:start_child_dist([ListenSocket, EmOpts, SslOpts]).
+
+get_emulated_opts(TrackerPid) ->
+ call(TrackerPid, get_emulated_opts).
+set_emulated_opts(TrackerPid, InetValues) ->
+ call(TrackerPid, {set_emulated_opts, InetValues}).
+get_all_opts(TrackerPid) ->
+ call(TrackerPid, get_all_opts).
+
+%%====================================================================
+%% ssl_listen_tracker_sup API
+%%====================================================================
+
+start_link(Port, SockOpts, SslOpts) ->
+ gen_server:start_link(?MODULE, [Port, SockOpts, SslOpts], []).
+
+%%--------------------------------------------------------------------
+-spec init(list()) -> {ok, #state{}}.
+%% Possible return values not used now.
+%% | {ok, #state{}, timeout()} | ignore | {stop, term()}.
+%%
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init([Port, Opts, SslOpts]) ->
+ process_flag(trap_exit, true),
+ true = link(Port),
+ {ok, #state{emulated_opts = Opts, port = Port, ssl_opts = SslOpts}}.
+
+%%--------------------------------------------------------------------
+-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({set_emulated_opts, Opts0}, _From,
+ #state{emulated_opts = Opts1} = State) ->
+ Opts = do_set_emulated_opts(Opts0, Opts1),
+ {reply, ok, State#state{emulated_opts = Opts}};
+handle_call(get_emulated_opts, _From,
+ #state{emulated_opts = Opts} = State) ->
+ {reply, {ok, Opts}, State};
+handle_call(get_all_opts, _From,
+ #state{emulated_opts = EmOpts,
+ ssl_opts = SslOpts} = State) ->
+ {reply, {ok, EmOpts, SslOpts}, 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(_, State)->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+-spec handle_info(msg(), #state{}) -> {stop, reason(), #state{}}.
+%% Possible return values not used now.
+%% {noreply, #state{}}.
+%% |{noreply, #state{}, timeout()} |
+%%
+%%
+%% Description: Handling all non call/cast messages
+%%-------------------------------------------------------------------
+handle_info({'EXIT', Port, _}, #state{port = Port} = State) ->
+ {stop, normal, 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(Pid, Msg) ->
+ gen_server:call(Pid, Msg, infinity).
+
+split_options(Opts) ->
+ split_options(Opts, emulated_options(), [], []).
+split_options([], _, SocketOpts, EmuOpts) ->
+ {SocketOpts, EmuOpts};
+split_options([{Name, _} = Opt | Opts], Emu, SocketOpts, EmuOpts) ->
+ case lists:member(Name, Emu) of
+ true ->
+ split_options(Opts, Emu, SocketOpts, [Opt | EmuOpts]);
+ false ->
+ split_options(Opts, Emu, [Opt | SocketOpts], EmuOpts)
+ end;
+split_options([Name | Opts], Emu, SocketOptNames, EmuOptNames) ->
+ case lists:member(Name, Emu) of
+ true ->
+ split_options(Opts, Emu, SocketOptNames, [Name | EmuOptNames]);
+ false ->
+ split_options(Opts, Emu, [Name | SocketOptNames], EmuOptNames)
+ end.
+
+do_set_emulated_opts([], Opts) ->
+ Opts;
+do_set_emulated_opts([{Name,_} = Opt | Rest], Opts) ->
+ do_set_emulated_opts(Rest, [Opt | proplists:delete(Name, Opts)]).
+
+get_socket_opts(_, [], _) ->
+ [];
+get_socket_opts(ListenSocket, SockOptNames, Cb) ->
+ {ok, Opts} = Cb:getopts(ListenSocket, SockOptNames),
+ Opts.
+
+get_emulated_opts(TrackerPid, EmOptNames) ->
+ {ok, EmOpts} = get_emulated_opts(TrackerPid),
+ lists:map(fun(Name) -> {value, Value} = lists:keysearch(Name, 1, EmOpts),
+ Value end,
+ EmOptNames).
diff --git a/lib/ssl/src/ssl_sup.erl b/lib/ssl/src/ssl_sup.erl
index e1aeb11ca4..7cccf8d5a5 100644
--- a/lib/ssl/src/ssl_sup.erl
+++ b/lib/ssl/src/ssl_sup.erl
@@ -47,8 +47,10 @@ init([]) ->
TLSConnetionManager = tls_connection_manager_child_spec(),
%% Not supported yet
%%DTLSConnetionManager = tls_connection_manager_child_spec(),
-
- {ok, {{one_for_all, 10, 3600}, [SessionCertManager, TLSConnetionManager]}}.
+ %% 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]}}.
manager_opts() ->
@@ -85,7 +87,7 @@ tls_connection_manager_child_spec() ->
StartFunc = {tls_connection_sup, start_link, []},
Restart = permanent,
Shutdown = 4000,
- Modules = [tls_connection, ssl_connection],
+ Modules = [tls_connection_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
@@ -98,6 +100,17 @@ tls_connection_manager_child_spec() ->
%% 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,
+ Shutdown = 4000,
+ Modules = [ssl_socket],
+ 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) ->
diff --git a/lib/ssl/src/ssl_v3.erl b/lib/ssl/src/ssl_v3.erl
index d477b3df81..68f7f5dee2 100644
--- a/lib/ssl/src/ssl_v3.erl
+++ b/lib/ssl/src/ssl_v3.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -130,7 +130,7 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) ->
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
ServerWriteKey, ClientIV, ServerIV}.
--spec suites() -> [cipher_suite()].
+-spec suites() -> [ssl_cipher:cipher_suite()].
suites() ->
[
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index ffa04ee8ba..2ab085321a 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -53,7 +53,7 @@
%% Alert and close handling
-export([send_alert/2, handle_own_alert/4, handle_close_alert/3,
handle_normal_shutdown/3, handle_unexpected_message/3,
- workaround_transport_delivery_problems/2, alert_user/5, alert_user/8
+ workaround_transport_delivery_problems/2, alert_user/6, alert_user/9
]).
%% Data handling
@@ -66,18 +66,18 @@
%% gen_fsm callbacks
-export([init/1, hello/2, certify/2, cipher/2,
abbreviated/2, connection/2, handle_event/3,
- handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
+ handle_sync_event/4, handle_info/3, terminate/3, code_change/4, format_status/2]).
%%====================================================================
%% Internal application API
%%====================================================================
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_} = Opts,
+start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts,
User, {CbModule, _,_, _} = CbInfo,
Timeout) ->
try
{ok, Pid} = tls_connection_sup:start_child([Role, Host, Port, Socket,
Opts, User, CbInfo]),
- {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule),
+ {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker),
ok = ssl_connection:handshake(SslSocket, Timeout),
{ok, SslSocket}
catch
@@ -85,13 +85,13 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_} = Opts,
Error
end;
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_} = Opts,
+start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} = Opts,
User, {CbModule, _,_, _} = CbInfo,
Timeout) ->
try
{ok, Pid} = tls_connection_sup:start_child_dist([Role, Host, Port, Socket,
Opts, User, CbInfo]),
- {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule),
+ {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker),
ok = ssl_connection:handshake(SslSocket, Timeout),
{ok, SslSocket}
catch
@@ -144,29 +144,10 @@ send_change_cipher(Msg, #state{connection_states = ConnectionStates0,
start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
{ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}.
-init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
+init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
process_flag(trap_exit, true),
- State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
- Handshake = ssl_handshake:init_handshake_history(),
- TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
- try ssl_config:init(SSLOpts0, Role) of
- {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} ->
- Session = State0#state.session,
- State = State0#state{
- tls_handshake_history = Handshake,
- session = Session#session{own_certificate = OwnCert,
- time_stamp = TimeStamp},
- file_ref_db = FileRefHandle,
- cert_db_ref = Ref,
- cert_db = CertDbHandle,
- session_cache = CacheHandle,
- private_key = Key,
- diffie_hellman_params = DHParams},
- gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State))
- catch
- throw:Error ->
- gen_fsm:enter_loop(?MODULE, [], error, {Error,State0}, get_timeout(State0))
- end.
+ State = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
+ gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State)).
%%--------------------------------------------------------------------
%% Description:There should be one instance of this function for each
@@ -208,11 +189,11 @@ hello(Hello = #client_hello{client_version = ClientVersion,
session_cache = Cache,
session_cache_cb = CacheCb,
ssl_options = SslOpts}) ->
- HashSign = ssl_handshake:select_hashsign(HashSigns, Cert),
case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert}, Renegotiation) of
{Version, {Type, Session},
ConnectionStates, ServerHelloExt} ->
+ HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version),
ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign},
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
@@ -342,8 +323,7 @@ handle_info(Msg, StateName, State) ->
%% Reason. The return value is ignored.
%%--------------------------------------------------------------------
terminate(Reason, StateName, State) ->
- ssl_connection:terminate(Reason, StateName, State).
-
+ catch ssl_connection:terminate(Reason, StateName, State).
%%--------------------------------------------------------------------
%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
@@ -352,6 +332,9 @@ terminate(Reason, StateName, State) ->
code_change(_OldVsn, StateName, State, _Extra) ->
{ok, StateName, State}.
+format_status(Type, Data) ->
+ ssl_connection:format_status(Type, Data).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -362,22 +345,13 @@ encode_handshake(Handshake, Version, ConnectionStates0, Hist0) ->
ssl_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).
-
-
decode_alerts(Bin) ->
- decode_alerts(Bin, []).
-
-decode_alerts(<<?BYTE(Level), ?BYTE(Description), Rest/binary>>, Acc) ->
- A = ?ALERT_REC(Level, Description),
- decode_alerts(Rest, [A | Acc]);
-decode_alerts(<<>>, Acc) ->
- lists:reverse(Acc, []).
+ ssl_alert:decode(Bin).
-initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
+initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
ConnectionStates = ssl_record:init_connection_states(Role),
@@ -391,9 +365,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
Monitor = erlang:monitor(process, User),
#state{socket_options = SocketOptions,
- %% We do not want to save the password in the state so that
- %% could be written in the clear into error logs.
- ssl_options = SSLOptions#ssl_options{password = undefined},
+ ssl_options = SSLOptions,
session = #session{is_resumable = new},
transport_cb = CbModule,
data_tag = DataTag,
@@ -411,7 +383,8 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
renegotiation = {false, first},
start_or_recv_from = undefined,
send_queue = queue:new(),
- protocol_cb = ?MODULE
+ protocol_cb = ?MODULE,
+ tracker = Tracker
}.
next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) ->
@@ -420,10 +393,13 @@ next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = S
next_state(_,Next, no_record, State) ->
{next_state, Next, State, get_timeout(State)};
-next_state(_,Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, State) ->
- Alerts = decode_alerts(EncAlerts),
- handle_alerts(Alerts, {next_state, Next, State, get_timeout(State)});
-
+next_state(Current, Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, #state{negotiated_version = Version} = State) ->
+ case decode_alerts(EncAlerts) of
+ Alerts = [_|_] ->
+ handle_alerts(Alerts, {next_state, Next, State, get_timeout(State)});
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, Current, State)
+ end;
next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
State0 = #state{protocol_buffers =
#protocol_buffers{tls_handshake_buffer = Buf0} = Buffers,
@@ -513,7 +489,7 @@ next_record(State) ->
next_record_if_active(State =
#state{socket_options =
- #socket_options{active = false}}) ->
+ #socket_options{active = false}}) ->
{no_record ,State};
next_record_if_active(State) ->
@@ -577,7 +553,8 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
bytes_to_read = BytesToRead,
start_or_recv_from = RecvFrom,
timer = Timer,
- user_data_buffer = Buffer0} = State0) ->
+ user_data_buffer = Buffer0,
+ tracker = Tracker} = State0) ->
Buffer1 = if
Buffer0 =:= <<>> -> Data;
Data =:= <<>> -> Buffer0;
@@ -585,7 +562,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
end,
case get_data(SOpts, BytesToRead, Buffer1) of
{ok, ClientData, Buffer} -> % Send data
- SocketOpt = deliver_app_data(Transport, Socket, SOpts, ClientData, Pid, RecvFrom),
+ 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,
@@ -606,7 +583,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
{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),
+ deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker),
{stop, normal, State0}
end.
@@ -661,8 +638,8 @@ decode_packet(Type, Buffer, PacketOpts) ->
%% 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) ->
- send_or_reply(Active, Pid, From, format_reply(Transport, Socket, SOpts, Data)),
+ 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)) ->
@@ -682,20 +659,20 @@ deliver_app_data(Transport, Socket, SOpts = #socket_options{active=Active, packe
end.
format_reply(_, _,#socket_options{active = false, mode = Mode, packet = Packet,
- header = Header}, Data) ->
+ 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) ->
- {ssl, ssl_socket:socket(self(), Transport, Socket, ?MODULE),
+ 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) ->
- send_or_reply(Active, Pid, From, format_packet_error(Transport, Socket, SO, 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) ->
+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) ->
- {ssl_error, ssl_socket:socket(self(), Transport, Socket, ?MODULE),
+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
@@ -751,7 +728,11 @@ handle_tls_handshake(Handle, StateName,
handle_tls_handshake(Handle, NextStateName, State);
{stop, _,_} = Stop ->
Stop
- end.
+ end;
+
+handle_tls_handshake(_Handle, _StateName, #state{}) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
write_application_data(Data0, From,
#state{socket = Socket,
negotiated_version = Version,
@@ -835,10 +816,10 @@ 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} = State) ->
+ role = Role, socket_options = Opts, tracker = Tracker} = State) ->
invalidate_session(Role, Host, Port, Session),
log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
- alert_user(Transport, Socket, StateName, Opts, Pid, From, Alert, Role),
+ alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role),
{stop, normal, State};
handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
@@ -859,36 +840,37 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
{Record, State} = next_record(State0),
next_state(StateName, connection, Record, State);
-handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName,
+%% Gracefully log and ignore all other warning alerts
+handle_alert(#alert{level = ?WARNING} = Alert, StateName,
#state{ssl_options = SslOpts} = State0) ->
log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
{Record, State} = next_record(State0),
next_state(StateName, StateName, Record, State).
-alert_user(Transport, Socket, connection, Opts, Pid, From, Alert, Role) ->
- alert_user(Transport,Socket, Opts#socket_options.active, Pid, From, Alert, Role);
-alert_user(Transport, Socket,_, _, _, From, Alert, Role) ->
- alert_user(Transport, Socket, From, Alert, Role).
+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, Socket, From, Alert, Role) ->
- alert_user(Transport, Socket, false, no_pid, 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) ->
+alert_user(_, _, _, false = Active, Pid, From, Alert, Role) ->
%% 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, Socket, Active, Pid, From, Alert, Role) ->
+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)});
+ Transport, Socket, ?MODULE, Tracker)});
ReasonCode ->
send_or_reply(Active, Pid, From,
{ssl_error, ssl_socket:socket(self(),
- Transport, Socket, ?MODULE), ReasonCode})
+ Transport, Socket, ?MODULE, Tracker), ReasonCode})
end.
log_alert(true, Info, Alert) ->
@@ -921,15 +903,17 @@ handle_own_alert(Alert, Version, StateName,
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, Socket, StartFrom, Alert, Role);
+ 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, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role).
+ alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role).
handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
diff --git a/lib/ssl/src/tls_connection_sup.erl b/lib/ssl/src/tls_connection_sup.erl
index 6f0d8a7262..7a637c212a 100644
--- a/lib/ssl/src/tls_connection_sup.erl
+++ b/lib/ssl/src/tls_connection_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -59,7 +59,7 @@ init(_O) ->
StartFunc = {tls_connection, start_link, []},
Restart = temporary, % E.g. should not be restarted
Shutdown = 4000,
- Modules = [tls_connection],
+ Modules = [tls_connection, ssl_connection],
Type = worker,
ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 01abefca46..183cabcfcd 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -33,6 +33,8 @@
-export([client_hello/8, hello/4,
get_tls_handshake/3, encode_handshake/2, decode_handshake/3]).
+-type tls_handshake() :: #client_hello{} | ssl_handshake:ssl_handshake().
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -71,11 +73,11 @@ client_hello(Host, Port, ConnectionStates,
#connection_states{} | {inet:port_number(), #session{}, db_handle(),
atom(), #connection_states{}, binary() | undefined},
boolean()) ->
- {tls_version(), session_id(), #connection_states{}, binary() | undefined}|
- {tls_version(), {resumed | new, #session{}}, #connection_states{},
- [binary()] | undefined,
- [oid()] | undefined, [oid()] | undefined} |
- #alert{}.
+ {tls_record:tls_version(), session_id(), #connection_states{}, binary() | undefined}|
+ {tls_record:tls_version(), {resumed | new, #session{}}, #connection_states{},
+ [binary()] | undefined,
+ [ssl_handshake:oid()] | undefined, [ssl_handshake:oid()] | undefined} |
+ #alert{}.
%%
%% Description: Handles a recieved hello message
%%--------------------------------------------------------------------
@@ -122,7 +124,7 @@ hello(#client_hello{client_version = ClientVersion,
end.
%%--------------------------------------------------------------------
--spec encode_handshake(tls_handshake(), tls_version()) -> iolist().
+-spec encode_handshake(tls_handshake(), tls_record:tls_version()) -> iolist().
%%
%% Description: Encode a handshake packet
%%--------------------------------------------------------------------x
@@ -132,7 +134,7 @@ encode_handshake(Package, Version) ->
[MsgType, ?uint24(Len), Bin].
%%--------------------------------------------------------------------
--spec get_tls_handshake(tls_version(), binary(), binary() | iolist()) ->
+-spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist()) ->
{[tls_handshake()], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
diff --git a/lib/ssl/src/tls_handshake.hrl b/lib/ssl/src/tls_handshake.hrl
index dbe930cb90..1646e5b6f2 100644
--- a/lib/ssl/src/tls_handshake.hrl
+++ b/lib/ssl/src/tls_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,6 +37,4 @@
extensions
}).
--type tls_handshake() :: #client_hello{} | ssl_handshake().
-
-endif. % -ifdef(tls_handshake).
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 8c0c4f3c91..f50ea22f39 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -44,6 +44,11 @@
highest_protocol_version/1, supported_protocol_versions/0,
is_acceptable_version/1, is_acceptable_version/2]).
+-export_type([tls_version/0, tls_atom_version/0]).
+
+-type tls_version() :: ssl_record:ssl_version().
+-type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'.
+
-compile(inline).
%%====================================================================
@@ -149,21 +154,24 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version,
sequence_number = Seq,
security_parameters = SecParams} = ReadState0,
CompressAlg = SecParams#security_parameters.compression_algorithm,
- {PlainFragment, Mac, ReadState1} = ssl_record:decipher(Version, CipherFragment, ReadState0),
- MacHash = calc_mac_hash(Type, Version, PlainFragment, ReadState1),
- case ssl_record:is_correct_mac(Mac, MacHash) of
- true ->
- {Plain, CompressionS1} = ssl_record:uncompress(CompressAlg,
- 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.
-
+ case ssl_record:decipher(Version, CipherFragment, ReadState0) 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(CompressAlg,
+ 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().
diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl
index 7c7fdd64c3..7a5f9c1b38 100644
--- a/lib/ssl/src/tls_v1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -166,7 +166,7 @@ setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize,
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
ServerWriteKey, ClientIV, ServerIV}.
--spec mac_hash(integer(), binary(), integer(), integer(), tls_version(),
+-spec mac_hash(integer(), binary(), integer(), integer(), tls_record:tls_version(),
integer(), binary()) -> binary().
mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
@@ -181,25 +181,9 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
Fragment]),
Mac.
--spec suites(1|2|3) -> [cipher_suite()].
-
-suites(Minor) when Minor == 1; Minor == 2->
- case sufficent_ec_support() of
- true ->
- all_suites(Minor);
- false ->
- no_ec_suites(Minor)
- end;
-
-suites(Minor) when Minor == 3 ->
- case sufficent_ec_support() of
- true ->
- all_suites(3) ++ all_suites(2);
- false ->
- no_ec_suites(3) ++ no_ec_suites(2)
- end.
-
-all_suites(Minor) when Minor == 1; Minor == 2->
+-spec suites(1|2|3) -> [ssl_cipher:cipher_suite()].
+
+suites(Minor) when Minor == 1; Minor == 2 ->
[
?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
@@ -235,7 +219,7 @@ all_suites(Minor) when Minor == 1; Minor == 2->
?TLS_RSA_WITH_DES_CBC_SHA
];
-all_suites(3) ->
+suites(3) ->
[
?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
@@ -254,33 +238,7 @@ all_suites(3) ->
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
?TLS_RSA_WITH_AES_128_CBC_SHA256
- ].
-
-no_ec_suites(Minor) when Minor == 1; Minor == 2->
- [
- ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
- ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
- ?TLS_RSA_WITH_AES_256_CBC_SHA,
- ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
- ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
- ?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
- ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
- ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
- ?TLS_RSA_WITH_AES_128_CBC_SHA,
- ?TLS_RSA_WITH_RC4_128_SHA,
- ?TLS_RSA_WITH_RC4_128_MD5,
- ?TLS_DHE_RSA_WITH_DES_CBC_SHA,
- ?TLS_RSA_WITH_DES_CBC_SHA
- ];
-no_ec_suites(3) ->
- [
- ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
- ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
- ?TLS_RSA_WITH_AES_256_CBC_SHA256,
- ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
- ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
- ?TLS_RSA_WITH_AES_128_CBC_SHA256
- ].
+ ] ++ suites(2).
%%--------------------------------------------------------------------
%%% Internal functions
@@ -439,8 +397,6 @@ enum_to_oid(24) -> ?secp384r1;
enum_to_oid(25) -> ?secp521r1;
enum_to_oid(26) -> ?brainpoolP256r1;
enum_to_oid(27) -> ?brainpoolP384r1;
-enum_to_oid(28) -> ?brainpoolP512r1.
-
-sufficent_ec_support() ->
- CryptoSupport = crypto:supports(),
- proplists:get_bool(ecdh, proplists:get_value(public_keys, CryptoSupport)).
+enum_to_oid(28) -> ?brainpoolP512r1;
+enum_to_oid(_) ->
+ undefined.
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 244eb5ce0a..2f8ff6f04e 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -39,6 +39,7 @@ MODULES = \
ssl_basic_SUITE \
ssl_cipher_SUITE \
ssl_certificate_verify_SUITE\
+ ssl_crl_SUITE\
ssl_dist_SUITE \
ssl_handshake_SUITE \
ssl_npn_hello_SUITE \
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 4603a9f846..15a7e118ff 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -18,23 +18,71 @@
%%
-module(make_certs).
+-compile([export_all]).
--export([all/2]).
+%-export([all/1, all/2, rootCA/2, intermediateCA/3, endusers/3, enduser/3, revoke/3, gencrl/2, verify/3]).
--record(dn, {commonName,
+-record(config, {commonName,
organizationalUnitName = "Erlang OTP",
organizationName = "Ericsson AB",
localityName = "Stockholm",
countryName = "SE",
- emailAddress = "[email protected]"}).
+ emailAddress = "[email protected]",
+ default_bits = 2048,
+ v2_crls = true,
+ ecc_certs = false,
+ issuing_distribution_point = false,
+ crl_port = 8000,
+ openssl_cmd = "openssl"}).
+
+
+default_config() ->
+ #config{}.
+
+make_config(Args) ->
+ make_config(Args, #config{}).
+
+make_config([], C) ->
+ C;
+make_config([{organizationalUnitName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{organizationalUnitName = Name});
+make_config([{organizationName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{organizationName = Name});
+make_config([{localityName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{localityName = Name});
+make_config([{countryName, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{countryName = Name});
+make_config([{emailAddress, Name}|T], C) when is_list(Name) ->
+ make_config(T, C#config{emailAddress = Name});
+make_config([{default_bits, Bits}|T], C) when is_integer(Bits) ->
+ make_config(T, C#config{default_bits = Bits});
+make_config([{v2_crls, Bool}|T], C) when is_boolean(Bool) ->
+ make_config(T, C#config{v2_crls = Bool});
+make_config([{crl_port, Port}|T], C) when is_integer(Port) ->
+ make_config(T, C#config{crl_port = Port});
+make_config([{ecc_certs, Bool}|T], C) when is_boolean(Bool) ->
+ make_config(T, C#config{ecc_certs = Bool});
+make_config([{issuing_distribution_point, Bool}|T], C) when is_boolean(Bool) ->
+ make_config(T, C#config{issuing_distribution_point = Bool});
+make_config([{openssl_cmd, Cmd}|T], C) when is_list(Cmd) ->
+ make_config(T, C#config{openssl_cmd = Cmd}).
+
+
+all([DataDir, PrivDir]) ->
+ all(DataDir, PrivDir).
all(DataDir, PrivDir) ->
- OpenSSLCmd = "openssl",
+ all(DataDir, PrivDir, #config{}).
+
+all(DataDir, PrivDir, C) when is_list(C) ->
+ all(DataDir, PrivDir, make_config(C));
+all(DataDir, PrivDir, C = #config{}) ->
+ ok = filelib:ensure_dir(filename:join(PrivDir, "erlangCA")),
create_rnd(DataDir, PrivDir), % For all requests
- rootCA(PrivDir, OpenSSLCmd, "erlangCA"),
- intermediateCA(PrivDir, OpenSSLCmd, "otpCA", "erlangCA"),
- endusers(PrivDir, OpenSSLCmd, "otpCA", ["client", "server"]),
- collect_certs(PrivDir, ["erlangCA", "otpCA"], ["client", "server"]),
+ rootCA(PrivDir, "erlangCA", C),
+ intermediateCA(PrivDir, "otpCA", "erlangCA", C),
+ endusers(PrivDir, "otpCA", ["client", "server", "revoked"], C),
+ endusers(PrivDir, "erlangCA", ["localhost"], C),
%% Create keycert files
SDir = filename:join([PrivDir, "server"]),
SC = filename:join([SDir, "cert.pem"]),
@@ -46,7 +94,14 @@ all(DataDir, PrivDir) ->
CK = filename:join([CDir, "key.pem"]),
CKC = filename:join([CDir, "keycert.pem"]),
append_files([CK, CC], CKC),
- remove_rnd(PrivDir).
+ RDir = filename:join([PrivDir, "revoked"]),
+ RC = filename:join([RDir, "cert.pem"]),
+ RK = filename:join([RDir, "key.pem"]),
+ RKC = filename:join([RDir, "keycert.pem"]),
+ revoke(PrivDir, "otpCA", "revoked", C),
+ append_files([RK, RC], RKC),
+ remove_rnd(PrivDir),
+ {ok, C}.
append_files(FileNames, ResultFileName) ->
{ok, ResultFile} = file:open(ResultFileName, [write]),
@@ -59,111 +114,176 @@ do_append_files([F|Fs], RF) ->
ok = file:write(RF, Data),
do_append_files(Fs, RF).
-rootCA(Root, OpenSSLCmd, Name) ->
- create_ca_dir(Root, Name, ca_cnf(Name)),
- DN = #dn{commonName = Name},
- create_self_signed_cert(Root, OpenSSLCmd, Name, req_cnf(DN)),
- ok.
+rootCA(Root, Name, C) ->
+ create_ca_dir(Root, Name, ca_cnf(C#config{commonName = Name})),
+ create_self_signed_cert(Root, Name, req_cnf(C#config{commonName = Name}), C),
+ file:copy(filename:join([Root, Name, "cert.pem"]), filename:join([Root, Name, "cacerts.pem"])),
+ gencrl(Root, Name, C).
-intermediateCA(Root, OpenSSLCmd, CA, ParentCA) ->
- CA = "otpCA",
- create_ca_dir(Root, CA, ca_cnf(CA)),
+intermediateCA(Root, CA, ParentCA, C) ->
+ create_ca_dir(Root, CA, ca_cnf(C#config{commonName = CA})),
CARoot = filename:join([Root, CA]),
- DN = #dn{commonName = CA},
CnfFile = filename:join([CARoot, "req.cnf"]),
- file:write_file(CnfFile, req_cnf(DN)),
+ file:write_file(CnfFile, req_cnf(C#config{commonName = CA})),
KeyFile = filename:join([CARoot, "private", "key.pem"]),
ReqFile = filename:join([CARoot, "req.pem"]),
- create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
+ create_req(Root, CnfFile, KeyFile, ReqFile, C),
CertFile = filename:join([CARoot, "cert.pem"]),
- sign_req(Root, OpenSSLCmd, ParentCA, "ca_cert", ReqFile, CertFile).
-
-endusers(Root, OpenSSLCmd, CA, Users) ->
- lists:foreach(fun(User) -> enduser(Root, OpenSSLCmd, CA, User) end, Users).
-
-enduser(Root, OpenSSLCmd, CA, User) ->
+ sign_req(Root, ParentCA, "ca_cert", ReqFile, CertFile, C),
+ CACertsFile = filename:join(CARoot, "cacerts.pem"),
+ file:copy(filename:join([Root, ParentCA, "cacerts.pem"]), CACertsFile),
+ %% append this CA's cert to the cacerts file
+ {ok, Bin} = file:read_file(CertFile),
+ {ok, FD} = file:open(CACertsFile, [append]),
+ file:write(FD, ["\n", Bin]),
+ file:close(FD),
+ gencrl(Root, CA, C).
+
+endusers(Root, CA, Users, C) ->
+ [enduser(Root, CA, User, C) || User <- Users].
+
+enduser(Root, CA, User, C) ->
UsrRoot = filename:join([Root, User]),
file:make_dir(UsrRoot),
CnfFile = filename:join([UsrRoot, "req.cnf"]),
- DN = #dn{commonName = User},
- file:write_file(CnfFile, req_cnf(DN)),
+ file:write_file(CnfFile, req_cnf(C#config{commonName = User})),
KeyFile = filename:join([UsrRoot, "key.pem"]),
ReqFile = filename:join([UsrRoot, "req.pem"]),
- create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
+ create_req(Root, CnfFile, KeyFile, ReqFile, C),
+ %create_req(Root, CnfFile, KeyFile, ReqFile),
CertFileAllUsage = filename:join([UsrRoot, "cert.pem"]),
- sign_req(Root, OpenSSLCmd, CA, "user_cert", ReqFile, CertFileAllUsage),
+ sign_req(Root, CA, "user_cert", ReqFile, CertFileAllUsage, C),
CertFileDigitalSigOnly = filename:join([UsrRoot, "digital_signature_only_cert.pem"]),
- sign_req(Root, OpenSSLCmd, CA, "user_cert_digital_signature_only", ReqFile, CertFileDigitalSigOnly).
-
-collect_certs(Root, CAs, Users) ->
- Bins = lists:foldr(
- fun(CA, Acc) ->
- File = filename:join([Root, CA, "cert.pem"]),
- {ok, Bin} = file:read_file(File),
- [Bin, "\n" | Acc]
- end, [], CAs),
- lists:foreach(
- fun(User) ->
- File = filename:join([Root, User, "cacerts.pem"]),
- file:write_file(File, Bins)
- end, Users).
+ sign_req(Root, CA, "user_cert_digital_signature_only", ReqFile, CertFileDigitalSigOnly, C),
+ CACertsFile = filename:join(UsrRoot, "cacerts.pem"),
+ file:copy(filename:join([Root, CA, "cacerts.pem"]), CACertsFile),
+ ok.
+
+revoke(Root, CA, User, C) ->
+ UsrCert = filename:join([Root, User, "cert.pem"]),
+ CACnfFile = filename:join([Root, CA, "ca.cnf"]),
+ Cmd = [C#config.openssl_cmd, " ca"
+ " -revoke ", UsrCert,
+ [" -crl_reason keyCompromise" || C#config.v2_crls ],
+ " -config ", CACnfFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env),
+ gencrl(Root, CA, C).
+
+gencrl(Root, CA, C) ->
+ CACnfFile = filename:join([Root, CA, "ca.cnf"]),
+ CACRLFile = filename:join([Root, CA, "crl.pem"]),
+ Cmd = [C#config.openssl_cmd, " ca"
+ " -gencrl ",
+ " -crlhours 24",
+ " -out ", CACRLFile,
+ " -config ", CACnfFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env).
-create_self_signed_cert(Root, OpenSSLCmd, CAName, Cnf) ->
+verify(Root, CA, User, C) ->
+ CAFile = filename:join([Root, User, "cacerts.pem"]),
+ CACRLFile = filename:join([Root, CA, "crl.pem"]),
+ CertFile = filename:join([Root, User, "cert.pem"]),
+ Cmd = [C#config.openssl_cmd, " verify"
+ " -CAfile ", CAFile,
+ " -CRLfile ", CACRLFile, %% this is undocumented, but seems to work
+ " -crl_check ",
+ CertFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ try cmd(Cmd, Env) catch
+ exit:{eval_cmd, _, _} ->
+ invalid
+ end.
+
+create_self_signed_cert(Root, CAName, Cnf, C = #config{ecc_certs = true}) ->
CARoot = filename:join([Root, CAName]),
CnfFile = filename:join([CARoot, "req.cnf"]),
file:write_file(CnfFile, Cnf),
KeyFile = filename:join([CARoot, "private", "key.pem"]),
CertFile = filename:join([CARoot, "cert.pem"]),
- Cmd = [OpenSSLCmd, " req"
+ Cmd = [C#config.openssl_cmd, " ecparam"
+ " -out ", KeyFile,
+ " -name secp521r1 ",
+ %" -name sect283k1 ",
+ " -genkey "],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env),
+
+ Cmd2 = [C#config.openssl_cmd, " req"
" -new"
" -x509"
" -config ", CnfFile,
- " -keyout ", KeyFile,
+ " -key ", KeyFile,
+ " -outform PEM ",
" -out ", CertFile],
- Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env),
- fix_key_file(OpenSSLCmd, KeyFile).
-
-% openssl 1.0 generates key files in pkcs8 format by default and we don't handle this format
-fix_key_file(OpenSSLCmd, KeyFile) ->
- KeyFileTmp = KeyFile ++ ".tmp",
- Cmd = [OpenSSLCmd, " rsa",
- " -in ",
- KeyFile,
- " -out ",
- KeyFileTmp],
- cmd(Cmd, []),
- ok = file:rename(KeyFileTmp, KeyFile).
+ cmd(Cmd2, Env);
+create_self_signed_cert(Root, CAName, Cnf, C) ->
+ CARoot = filename:join([Root, CAName]),
+ CnfFile = filename:join([CARoot, "req.cnf"]),
+ file:write_file(CnfFile, Cnf),
+ KeyFile = filename:join([CARoot, "private", "key.pem"]),
+ CertFile = filename:join([CARoot, "cert.pem"]),
+ Cmd = [C#config.openssl_cmd, " req"
+ " -new"
+ " -x509"
+ " -config ", CnfFile,
+ " -keyout ", KeyFile,
+ " -outform PEM",
+ " -out ", CertFile],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env).
+
create_ca_dir(Root, CAName, Cnf) ->
CARoot = filename:join([Root, CAName]),
+ ok = filelib:ensure_dir(CARoot),
file:make_dir(CARoot),
create_dirs(CARoot, ["certs", "crl", "newcerts", "private"]),
create_rnd(Root, filename:join([CAName, "private"])),
create_files(CARoot, [{"serial", "01\n"},
+ {"crlnumber", "01"},
{"index.txt", ""},
{"ca.cnf", Cnf}]).
-create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile) ->
- Cmd = [OpenSSLCmd, " req"
+create_req(Root, CnfFile, KeyFile, ReqFile, C = #config{ecc_certs = true}) ->
+ Cmd = [C#config.openssl_cmd, " ecparam"
+ " -out ", KeyFile,
+ " -name secp521r1 ",
+ %" -name sect283k1 ",
+ " -genkey "],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env),
+ Cmd2 = [C#config.openssl_cmd, " req"
+ " -new ",
+ " -key ", KeyFile,
+ " -outform PEM ",
+ " -out ", ReqFile,
+ " -config ", CnfFile],
+ cmd(Cmd2, Env);
+ %fix_key_file(KeyFile).
+create_req(Root, CnfFile, KeyFile, ReqFile, C) ->
+ Cmd = [C#config.openssl_cmd, " req"
" -new"
" -config ", CnfFile,
+ " -outform PEM ",
" -keyout ", KeyFile,
" -out ", ReqFile],
- Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env),
- fix_key_file(OpenSSLCmd, KeyFile).
+ Env = [{"ROOTDIR", filename:absname(Root)}],
+ cmd(Cmd, Env).
+ %fix_key_file(KeyFile).
+
-sign_req(Root, OpenSSLCmd, CA, CertType, ReqFile, CertFile) ->
+sign_req(Root, CA, CertType, ReqFile, CertFile, C) ->
CACnfFile = filename:join([Root, CA, "ca.cnf"]),
- Cmd = [OpenSSLCmd, " ca"
+ Cmd = [C#config.openssl_cmd, " ca"
" -batch"
" -notext"
" -config ", CACnfFile,
" -extensions ", CertType,
" -in ", ReqFile,
" -out ", CertFile],
- Env = [{"ROOTDIR", Root}],
+ Env = [{"ROOTDIR", filename:absname(Root)}],
cmd(Cmd, Env).
%%
@@ -194,19 +314,19 @@ cmd(Cmd, Env) ->
FCmd = lists:flatten(Cmd),
Port = open_port({spawn, FCmd}, [stream, eof, exit_status, stderr_to_stdout,
{env, Env}]),
- eval_cmd(Port).
+ eval_cmd(Port, FCmd).
-eval_cmd(Port) ->
+eval_cmd(Port, Cmd) ->
receive
{Port, {data, _}} ->
- eval_cmd(Port);
+ eval_cmd(Port, Cmd);
{Port, eof} ->
ok
end,
receive
{Port, {exit_status, Status}} when Status /= 0 ->
%% io:fwrite("exit status: ~w~n", [Status]),
- exit({eval_cmd, Status})
+ exit({eval_cmd, Cmd, Status})
after 0 ->
ok
end.
@@ -215,7 +335,7 @@ eval_cmd(Port) ->
%% Contents of configuration files
%%
-req_cnf(DN) ->
+req_cnf(C) ->
["# Purpose: Configuration for requests (end users and CAs)."
"\n"
"ROOTDIR = $ENV::ROOTDIR\n"
@@ -224,10 +344,10 @@ req_cnf(DN) ->
"[req]\n"
"input_password = secret\n"
"output_password = secret\n"
- "default_bits = 1024\n"
+ "default_bits = ", integer_to_list(C#config.default_bits), "\n"
"RANDFILE = $ROOTDIR/RAND\n"
"encrypt_key = no\n"
- "default_md = sha1\n"
+ "default_md = md5\n"
"#string_mask = pkix\n"
"x509_extensions = ca_ext\n"
"prompt = no\n"
@@ -235,12 +355,12 @@ req_cnf(DN) ->
"\n"
"[name]\n"
- "commonName = ", DN#dn.commonName, "\n"
- "organizationalUnitName = ", DN#dn.organizationalUnitName, "\n"
- "organizationName = ", DN#dn.organizationName, "\n"
- "localityName = ", DN#dn.localityName, "\n"
- "countryName = ", DN#dn.countryName, "\n"
- "emailAddress = ", DN#dn.emailAddress, "\n"
+ "commonName = ", C#config.commonName, "\n"
+ "organizationalUnitName = ", C#config.organizationalUnitName, "\n"
+ "organizationName = ", C#config.organizationName, "\n"
+ "localityName = ", C#config.localityName, "\n"
+ "countryName = ", C#config.countryName, "\n"
+ "emailAddress = ", C#config.emailAddress, "\n"
"\n"
"[ca_ext]\n"
@@ -249,8 +369,7 @@ req_cnf(DN) ->
"subjectKeyIdentifier = hash\n"
"subjectAltName = email:copy\n"].
-
-ca_cnf(CA) ->
+ca_cnf(C) ->
["# Purpose: Configuration for CAs.\n"
"\n"
"ROOTDIR = $ENV::ROOTDIR\n"
@@ -258,21 +377,23 @@ ca_cnf(CA) ->
"\n"
"[ca]\n"
- "dir = $ROOTDIR/", CA, "\n"
+ "dir = $ROOTDIR/", C#config.commonName, "\n"
"certs = $dir/certs\n"
"crl_dir = $dir/crl\n"
"database = $dir/index.txt\n"
"new_certs_dir = $dir/newcerts\n"
"certificate = $dir/cert.pem\n"
"serial = $dir/serial\n"
- "crl = $dir/crl.pem\n"
+ "crl = $dir/crl.pem\n",
+ ["crlnumber = $dir/crlnumber\n" || C#config.v2_crls],
"private_key = $dir/private/key.pem\n"
"RANDFILE = $dir/private/RAND\n"
"\n"
- "x509_extensions = user_cert\n"
+ "x509_extensions = user_cert\n",
+ ["crl_extensions = crl_ext\n" || C#config.v2_crls],
"unique_subject = no\n"
"default_days = 3600\n"
- "default_md = sha1\n"
+ "default_md = md5\n"
"preserve = no\n"
"policy = policy_match\n"
"\n"
@@ -286,6 +407,13 @@ ca_cnf(CA) ->
"emailAddress = supplied\n"
"\n"
+ "[crl_ext]\n"
+ "authorityKeyIdentifier=keyid:always,issuer:always\n",
+ ["issuingDistributionPoint=critical, @idpsec\n" || C#config.issuing_distribution_point],
+
+ "[idpsec]\n"
+ "fullname=URI:http://localhost:8000/",C#config.commonName,"/crl.pem\n"
+
"[user_cert]\n"
"basicConstraints = CA:false\n"
"keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n"
@@ -293,6 +421,12 @@ ca_cnf(CA) ->
"authorityKeyIdentifier = keyid,issuer:always\n"
"subjectAltName = email:copy\n"
"issuerAltName = issuer:copy\n"
+ "crlDistributionPoints=@crl_section\n"
+
+ "[crl_section]\n"
+ %% intentionally invalid
+ "URI.1=http://localhost/",C#config.commonName,"/crl.pem\n"
+ "URI.2=http://localhost:",integer_to_list(C#config.crl_port),"/",C#config.commonName,"/crl.pem\n"
"\n"
"[user_cert_digital_signature_only]\n"
@@ -310,4 +444,7 @@ ca_cnf(CA) ->
"subjectKeyIdentifier = hash\n"
"authorityKeyIdentifier = keyid:always,issuer:always\n"
"subjectAltName = email:copy\n"
- "issuerAltName = issuer:copy\n"].
+ "issuerAltName = issuer:copy\n"
+ "crlDistributionPoints=@crl_section\n"
+ ].
+
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 523760aba6..2f440f1f3c 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -96,6 +96,7 @@ basic_tests() ->
options_tests() ->
[der_input,
misc_ssl_options,
+ ssl_options_not_proplist,
socket_options,
invalid_inet_get_option,
invalid_inet_get_option_not_list,
@@ -114,7 +115,10 @@ options_tests() ->
reuseaddr,
tcp_reuseaddr,
honor_server_cipher_order,
- honor_client_cipher_order
+ honor_client_cipher_order,
+ ciphersuite_vs_version,
+ unordered_protocol_versions_server,
+ unordered_protocol_versions_client
].
api_tests() ->
@@ -136,7 +140,9 @@ api_tests() ->
ssl_accept_timeout,
ssl_recv_timeout,
versions_option,
- server_name_indication_option
+ server_name_indication_option,
+ accept_pool,
+ new_options_in_accept
].
session_tests() ->
@@ -185,7 +191,11 @@ error_handling_tests()->
tcp_error_propagation_in_active_mode,
tcp_connect,
tcp_connect_big,
- close_transport_accept
+ close_transport_accept,
+ recv_active,
+ recv_active_once,
+ recv_error_handling,
+ dont_crash_on_handshake_garbage
].
rizzo_tests() ->
@@ -238,6 +248,14 @@ end_per_group(_GroupName, Config) ->
Config.
%%--------------------------------------------------------------------
+init_per_testcase(Case, Config) when Case == unordered_protocol_versions_client;
+ Case == unordered_protocol_versions_server->
+ case proplists:get_value(supported, ssl:versions()) of
+ ['tlsv1.2' | _] ->
+ Config;
+ _ ->
+ {skip, "TLS 1.2 need but not supported on this platform"}
+ end;
init_per_testcase(no_authority_key_identifier, Config) ->
%% Clear cach so that root cert will not
%% be found.
@@ -308,7 +326,11 @@ alerts(Config) when is_list(Config) ->
?ILLEGAL_PARAMETER, ?UNKNOWN_CA, ?ACCESS_DENIED, ?DECODE_ERROR,
?DECRYPT_ERROR, ?EXPORT_RESTRICTION, ?PROTOCOL_VERSION,
?INSUFFICIENT_SECURITY, ?INTERNAL_ERROR, ?USER_CANCELED,
- ?NO_RENEGOTIATION],
+ ?NO_RENEGOTIATION, ?UNSUPPORTED_EXTENSION, ?CERTIFICATE_UNOBTAINABLE,
+ ?UNRECOGNISED_NAME, ?BAD_CERTIFICATE_STATUS_RESPONSE,
+ ?BAD_CERTIFICATE_HASH_VALUE, ?UNKNOWN_PSK_IDENTITY,
+ 255 %% Unsupported/unknow alert will result in a description too
+ ],
Alerts = [?ALERT_REC(?WARNING, ?CLOSE_NOTIFY) |
[?ALERT_REC(?FATAL, Desc) || Desc <- Descriptions]],
lists:foreach(fun(Alert) ->
@@ -320,6 +342,38 @@ alerts(Config) when is_list(Config) ->
end
end, Alerts).
%%--------------------------------------------------------------------
+new_options_in_accept() ->
+ [{doc,"Test that you can set ssl options in ssl_accept/3 and not tcp upgrade"}].
+new_options_in_accept(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts0 = ?config(server_dsa_opts, Config),
+ [_ , _ | ServerSslOpts] = ?config(server_opts, Config), %% Remove non ssl opts
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {ssl_extra_opts, [{versions, [sslv3]},
+ {ciphers,[{rsa,rc4_128,sha}]} | ServerSslOpts]}, %% To be set in ssl_accept/3
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, proplists:delete(cacertfile, ServerOpts0)}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, [{versions, [sslv3]} | ClientOpts]}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ServerMsg = ClientMsg = {ok, {sslv3, {rsa, rc4_128, sha}}},
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+
connection_info() ->
[{doc,"Test the API function ssl:connection_info/1"}].
connection_info(Config) when is_list(Config) ->
@@ -359,6 +413,7 @@ protocol_versions() ->
protocol_versions(Config) when is_list(Config) ->
basic_test(Config).
+
%%--------------------------------------------------------------------
empty_protocol_versions() ->
[{doc,"Test to set an empty list of protocol versions in app environment."}].
@@ -990,7 +1045,7 @@ misc_ssl_options(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- %% Chek that ssl options not tested elsewhere are filtered away e.i. not passed to inet.
+ %% Check that ssl options not tested elsewhere are filtered away e.i. not passed to inet.
TestOpts = [{depth, 1},
{key, undefined},
{password, []},
@@ -1018,6 +1073,17 @@ misc_ssl_options(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+ssl_options_not_proplist() ->
+ [{doc,"Test what happens if an option is not a key value tuple"}].
+
+ssl_options_not_proplist(Config) when is_list(Config) ->
+ BadOption = {client_preferred_next_protocols,
+ client, [<<"spdy/3">>,<<"http/1.1">>], <<"http/1.1">>},
+ {option_not_a_key_value_tuple, BadOption} =
+ ssl:connect("twitter.com", 443, [binary, {active, false},
+ BadOption]).
+
+%%--------------------------------------------------------------------
versions() ->
[{doc,"Test API function versions/0"}].
@@ -1106,6 +1172,57 @@ close_transport_accept(Config) when is_list(Config) ->
Other ->
exit({?LINE, Other})
end.
+%%--------------------------------------------------------------------
+recv_active() ->
+ [{doc,"Test recv on active socket"}].
+
+recv_active(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active, []}},
+ {options, [{active, true} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active, []}},
+ {options, [{active, true} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+recv_active_once() ->
+ [{doc,"Test recv on active socket"}].
+
+recv_active_once(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active_once, []}},
+ {options, [{active, once} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active_once, []}},
+ {options, [{active, once} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
dh_params() ->
@@ -1129,7 +1246,7 @@ dh_params(Config) when is_list(Config) ->
{from, self()},
{mfa, {ssl_test_lib, send_recv_result_active, []}},
{options,
- [{ciphers,[{dhe_rsa,aes_256_cbc,sha,ignore}]} |
+ [{ciphers,[{dhe_rsa,aes_256_cbc,sha}]} |
ClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
@@ -1228,7 +1345,7 @@ tcp_connect() ->
tcp_connect(Config) when is_list(Config) ->
ServerOpts = ?config(server_opts, Config),
{_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- TcpOpts = [binary, {reuseaddr, true}],
+ TcpOpts = [binary, {reuseaddr, true}, {active, false}],
Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
{from, self()},
@@ -2511,6 +2628,81 @@ honor_cipher_order(Config, Honor, ServerCiphers, ClientCiphers, Expected) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+ciphersuite_vs_version(Config) when is_list(Config) ->
+
+ {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]),
+ ok = gen_tcp:send(Socket,
+ <<22, 3,0, 49:16, % handshake, SSL 3.0, length
+ 1, 45:24, % client_hello, length
+ 3,0, % SSL 3.0
+ 16#deadbeef:256, % 32 'random' bytes = 256 bits
+ 0, % no session ID
+ %% three cipher suites -- null, one with sha256 hash and one with sha hash
+ 6:16, 0,255, 0,61, 0,57,
+ 1, 0 % no compression
+ >>),
+ {ok, <<22, RecMajor:8, RecMinor:8, _RecLen:16, 2, HelloLen:24>>} = gen_tcp:recv(Socket, 9, 10000),
+ {ok, <<HelloBin:HelloLen/binary>>} = gen_tcp:recv(Socket, HelloLen, 5000),
+ ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin),
+ case ServerHello of
+ #server_hello{server_version = {3,0}, cipher_suite = <<0,57>>} ->
+ ok;
+ _ ->
+ ct:fail({unexpected_server_hello, ServerHello})
+ end.
+
+%%--------------------------------------------------------------------
+
+dont_crash_on_handshake_garbage() ->
+ [{doc, "Ensure SSL server worker thows an alert on garbage during handshake "
+ "instead of crashing and exposing state to user code"}].
+
+dont_crash_on_handshake_garbage(Config) ->
+ ServerOpts = ?config(server_opts, Config),
+
+ {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ unlink(Server), monitor(process, Server),
+ Port = ssl_test_lib:inet_port(Server),
+
+ {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]),
+
+ % Send hello and garbage record
+ ok = gen_tcp:send(Socket,
+ [<<22, 3,3, 49:16, 1, 45:24, 3,3, % client_hello
+ 16#deadbeef:256, % 32 'random' bytes = 256 bits
+ 0, 6:16, 0,255, 0,61, 0,57, 1, 0 >>, % some hello values
+
+ <<22, 3,3, 5:16, 92,64,37,228,209>> % garbage
+ ]),
+ % Send unexpected change_cipher_spec
+ ok = gen_tcp:send(Socket, <<20, 0,0,12, 111,40,244,7,137,224,16,109,197,110,249,152>>),
+
+ % Ensure we receive an alert, not sudden disconnect
+ {ok, <<21, _/binary>>} = drop_handshakes(Socket, 1000).
+
+drop_handshakes(Socket, Timeout) ->
+ {ok, <<RecType:8, _RecMajor:8, _RecMinor:8, RecLen:16>> = Header} = gen_tcp:recv(Socket, 5, Timeout),
+ {ok, <<Frag:RecLen/binary>>} = gen_tcp:recv(Socket, RecLen, Timeout),
+ case RecType of
+ 22 -> drop_handshakes(Socket, Timeout);
+ _ -> {ok, <<Header/binary, Frag/binary>>}
+ end.
+
+
+%%--------------------------------------------------------------------
hibernate() ->
[{doc,"Check that an SSL connection that is started with option "
@@ -2909,6 +3101,57 @@ versions_option(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
+unordered_protocol_versions_server() ->
+ [{doc,"Test that the highest protocol is selected even"
+ " when it is not first in the versions list."}].
+
+unordered_protocol_versions_server(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, ClientOpts}]),
+ CipherSuite = first_rsa_suite(ssl:cipher_suites()),
+ ServerMsg = ClientMsg = {ok, {'tlsv1.2', CipherSuite}},
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg).
+
+%%--------------------------------------------------------------------
+unordered_protocol_versions_client() ->
+ [{doc,"Test that the highest protocol is selected even"
+ " when it is not first in the versions list."}].
+
+unordered_protocol_versions_client(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, ServerOpts }]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ClientOpts]}]),
+
+ CipherSuite = first_rsa_suite(ssl:cipher_suites()),
+ ServerMsg = ClientMsg = {ok, {'tlsv1.2', CipherSuite}},
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg).
+
+%%--------------------------------------------------------------------
server_name_indication_option() ->
[{doc,"Test API server_name_indication option to connect."}].
@@ -2946,6 +3189,53 @@ server_name_indication_option(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client0),
ssl_test_lib:close(Client1).
+%%--------------------------------------------------------------------
+
+accept_pool() ->
+ [{doc,"Test having an accept pool."}].
+accept_pool(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server0 = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {accepters, 3},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server0),
+ [Server1, Server2] = ssl_test_lib:accepters(2),
+
+ Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}
+ ]),
+
+ Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}
+ ]),
+
+ Client2 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}
+ ]),
+
+ ssl_test_lib:check_ok([Server0, Server1, Server2, Client0, Client1, Client2]),
+
+ ssl_test_lib:close(Server0),
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Server2),
+ ssl_test_lib:close(Client0),
+ ssl_test_lib:close(Client1),
+ ssl_test_lib:close(Client2).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
@@ -3406,7 +3696,7 @@ run_suites(Ciphers, Version, Config, Type) ->
Result = lists:map(fun(Cipher) ->
cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end,
- Ciphers),
+ ssl_test_lib:filter_suites(Ciphers)),
case lists:flatten(Result) of
[] ->
ok;
@@ -3457,6 +3747,10 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
connection_info_result(Socket) ->
ssl:connection_info(Socket).
+version_info_result(Socket) ->
+ {ok, {Version, _}} = ssl:connection_info(Socket),
+ {ok, Version}.
+
connect_dist_s(S) ->
Msg = term_to_binary({erlang,term}),
ok = ssl:send(S, Msg).
@@ -3534,3 +3828,22 @@ version_option_test(Config, Version) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+
+try_recv_active(Socket) ->
+ ssl:send(Socket, "Hello world"),
+ {error, einval} = ssl:recv(Socket, 11),
+ ok.
+try_recv_active_once(Socket) ->
+ {error, einval} = ssl:recv(Socket, 11),
+ ok.
+
+first_rsa_suite([{ecdhe_rsa, _, _} = Suite | _]) ->
+ Suite;
+first_rsa_suite([{dhe_rsa, _, _} = Suite| _]) ->
+ Suite;
+first_rsa_suite([{rsa, _, _} = Suite| _]) ->
+ Suite;
+first_rsa_suite([_ | Rest]) ->
+ first_rsa_suite(Rest).
+
+
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
new file mode 100644
index 0000000000..bad0949ec4
--- /dev/null
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -0,0 +1,542 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ssl_crl_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-define(TIMEOUT, 120000).
+-define(LONG_TIMEOUT, 600000).
+-define(SLEEP, 1000).
+-define(OPENSSL_RENEGOTIATE, "R\n").
+-define(OPENSSL_QUIT, "Q\n").
+-define(OPENSSL_GARBAGE, "P\n").
+-define(EXPIRE, 10).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ {group, basic},
+ {group, v1_crl},
+ {group, idp_crl}
+ ].
+
+groups() ->
+ [{basic, [], basic_tests()},
+ {v1_crl, [], v1_crl_tests()},
+ {idp_crl, [], idp_crl_tests()}].
+
+basic_tests() ->
+ [crl_verify_valid, crl_verify_revoked].
+
+v1_crl_tests() ->
+ [crl_verify_valid, crl_verify_revoked].
+
+idp_crl_tests() ->
+ [crl_verify_valid, crl_verify_revoked].
+
+%%%================================================================
+%%% Suite init/end
+
+init_per_suite(Config0) ->
+ Dog = ct:timetrap(?LONG_TIMEOUT *2),
+ case os:find_executable("openssl") of
+ false ->
+ {skip, "Openssl not found"};
+ _ ->
+ TLSVersion = ?config(tls_version, Config0),
+ OpenSSL_version = (catch os:cmd("openssl version")),
+ ct:log("TLS version: ~p~nOpenSSL version: ~p~n~n~p:module_info(): ~p~n~nssl:module_info(): ~p~n",
+ [TLSVersion, OpenSSL_version, ?MODULE, ?MODULE:module_info(), ssl:module_info()]),
+ case ssl_test_lib:enough_openssl_crl_support(OpenSSL_version) of
+ false ->
+ {skip, io_lib:format("Bad openssl version: ~p",[OpenSSL_version])};
+ _ ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl:start(),
+ {ok, Hostname0} = inet:gethostname(),
+ IPfamily =
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts,[])) of
+ true -> inet6;
+ false -> inet
+ end,
+ [{ipfamily,IPfamily}, {watchdog, Dog}, {openssl_version,OpenSSL_version} | Config0]
+ catch _C:_E ->
+ ct:log("crypto:start() caught ~p:~p",[_C,_E]),
+ {skip, "Crypto did not start"}
+ end
+ end
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+%%%================================================================
+%%% Group init/end
+
+init_per_group(Group, Config) ->
+ ssl:start(),
+ inets:start(),
+ CertDir = filename:join(?config(priv_dir, Config), Group),
+ DataDir = ?config(data_dir, Config),
+ ServerRoot = make_dir_path([?config(priv_dir,Config), Group, tmp]),
+ %% start a HTTP server to serve the CRLs
+ {ok, Httpd} = inets:start(httpd, [{ipfamily, ?config(ipfamily,Config)},
+ {server_name, "localhost"}, {port, 0},
+ {server_root, ServerRoot},
+ {document_root, CertDir},
+ {modules, [mod_get]}
+ ]),
+ [{port,Port}] = httpd:info(Httpd, [port]),
+ ct:log("~p:~p~nHTTPD IP family=~p, port=~p~n", [?MODULE, ?LINE, ?config(ipfamily,Config), Port]),
+ CertOpts = [{crl_port,Port}|cert_opts(Group)],
+ Result = make_certs:all(DataDir, CertDir, CertOpts),
+ ct:log("~p:~p~nmake_certs:all(~n DataDir=~p,~n CertDir=~p,~n ServerRoot=~p~n Opts=~p~n) returned ~p~n", [?MODULE,?LINE,DataDir, CertDir, ServerRoot, CertOpts, Result]),
+ [{make_cert_result, Result}, {cert_dir, CertDir}, {httpd, Httpd} | Config].
+
+cert_opts(v1_crl) -> [{v2_crls, false}];
+cert_opts(idp_crl) -> [{issuing_distribution_point, true}];
+cert_opts(_) -> [].
+
+make_dir_path(PathComponents) ->
+ lists:foldl(fun(F,P0) -> file:make_dir(P=filename:join(P0,F)), P end,
+ "",
+ PathComponents).
+
+
+end_per_group(_GroupName, Config) ->
+ case ?config(httpd, Config) of
+ undefined -> ok;
+ Pid ->
+ ct:log("Stop httpd ~p",[Pid]),
+ ok = inets:stop(httpd, Pid)
+ ,ct:log("Stopped",[])
+ end,
+ inets:stop(),
+ Config.
+
+%%%================================================================
+%%% Test cases
+
+crl_verify_valid() ->
+ [{doc,"Verify a simple valid CRL chain"}].
+crl_verify_valid(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ PrivDir = ?config(cert_dir, Config),
+ ServerOpts = [{keyfile, filename:join([PrivDir, "server", "key.pem"])},
+ {certfile, filename:join([PrivDir, "server", "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])}],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ %{mfa, {ssl_test_lib, no_result, []}},
+ {options, ServerOpts}]),
+ ct:log("~p:~p~nreturn from ssl_test_lib:start_server:~n~p",[?MODULE,?LINE,Server]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ CACerts = load_cert(filename:join([PrivDir, "erlangCA", "cacerts.pem"])),
+
+ ClientOpts = [{cacerts, CACerts},
+ {verify, verify_peer},
+ {verify_fun, {fun validate_function/3, {CACerts, []}}}],
+
+
+ ct:log("~p:~p~ncalling ssl_test_lib:start_client",[?MODULE,?LINE]),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_send, [Data]}},
+ %{mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+ ct:log("~p:~p~nreturn from ssl_test_lib:start_client:~n~p",[?MODULE,?LINE,Client]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+crl_verify_revoked() ->
+ [{doc,"Verify a simple valid CRL chain"}].
+crl_verify_revoked(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ PrivDir = ?config(cert_dir, Config),
+ ServerOpts = [{keyfile, filename:join([PrivDir, "revoked", "key.pem"])},
+ {certfile, filename:join([PrivDir, "revoked", "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, "revoked", "cacerts.pem"])}],
+ ct:log("~p:~p~nserver opts ~p~n", [?MODULE,?LINE, ServerOpts]),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ %{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ CACerts = load_cert(filename:join([PrivDir, "erlangCA", "cacerts.pem"])),
+ ClientOpts = [{cacerts, CACerts},
+ {verify, verify_peer},
+ {verify_fun, {fun validate_function/3, {CACerts, []}}}],
+
+ {connect_failed, _} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ %{mfa, {?MODULE,
+ %erlang_ssl_receive, [Data]}},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close(Server),
+ process_flag(trap_exit, false).
+
+%%%================================================================
+%%% Lib
+
+erlang_ssl_receive(Socket, Data) ->
+ ct:log("~p:~p~nConnection info: ~p~n",
+ [?MODULE,?LINE, ssl:connection_info(Socket)]),
+ receive
+ {ssl, Socket, Data} ->
+ ct:log("~p:~p~nReceived ~p~n",[?MODULE,?LINE, Data]),
+ %% open_ssl server sometimes hangs waiting in blocking read
+ ssl:send(Socket, "Got it"),
+ ok;
+ {ssl, Socket, Byte} when length(Byte) == 1 ->
+ erlang_ssl_receive(Socket, tl(Data));
+ {Port, {data,Debug}} when is_port(Port) ->
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
+ erlang_ssl_receive(Socket,Data);
+ Other ->
+ ct:fail({unexpected_message, Other})
+ after 4000 ->
+ ct:fail({did_not_get, Data})
+ end.
+
+
+erlang_ssl_send(Socket, Data) ->
+ ct:log("~p:~p~nConnection info: ~p~n",
+ [?MODULE,?LINE, ssl:connection_info(Socket)]),
+ ssl:send(Socket, Data),
+ ok.
+
+load_certs(undefined) ->
+ undefined;
+load_certs(CertDir) ->
+ case file:list_dir(CertDir) of
+ {ok, Certs} ->
+ load_certs(lists:map(fun(Cert) -> filename:join(CertDir, Cert)
+ end, Certs), []);
+ {error, _} ->
+ undefined
+ end.
+
+load_certs([], Acc) ->
+ ct:log("~p:~p~nSuccessfully loaded ~p CA certificates~n", [?MODULE,?LINE, length(Acc)]),
+ Acc;
+load_certs([Cert|Certs], Acc) ->
+ case filelib:is_dir(Cert) of
+ true ->
+ load_certs(Certs, Acc);
+ _ ->
+ %ct:log("~p:~p~nLoading certificate ~p~n", [?MODULE,?LINE, Cert]),
+ load_certs(Certs, load_cert(Cert) ++ Acc)
+ end.
+
+load_cert(Cert) ->
+ {ok, Bin} = file:read_file(Cert),
+ case filename:extension(Cert) of
+ ".der" ->
+ %% no decoding necessary
+ [Bin];
+ _ ->
+ %% assume PEM otherwise
+ Contents = public_key:pem_decode(Bin),
+ [DER || {Type, DER, Cipher} <- Contents, Type == 'Certificate', Cipher == 'not_encrypted']
+ end.
+
+%% @doc Validator function for SSL negotiation.
+%%
+validate_function(Cert, valid_peer, State) ->
+ ct:log("~p:~p~nvaliding peer ~p with ~p intermediate certs~n",
+ [?MODULE,?LINE, get_common_name(Cert),
+ length(element(2, State))]),
+ %% peer certificate validated, now check the CRL
+ Res = (catch check_crl(Cert, State)),
+ ct:log("~p:~p~nCRL validate result for ~p: ~p~n",
+ [?MODULE,?LINE, get_common_name(Cert), Res]),
+ {Res, State};
+validate_function(Cert, valid, {TrustedCAs, IntermediateCerts}=State) ->
+ case public_key:pkix_is_self_signed(Cert) of
+ true ->
+ ct:log("~p:~p~nroot certificate~n",[?MODULE,?LINE]),
+ %% this is a root cert, no CRL
+ {valid, {TrustedCAs, [Cert|IntermediateCerts]}};
+ false ->
+ %% check is valid CA certificate, add to the list of
+ %% intermediates
+ Res = (catch check_crl(Cert, State)),
+ ct:log("~p:~p~nCRL intermediate CA validate result for ~p: ~p~n",
+ [?MODULE,?LINE, get_common_name(Cert), Res]),
+ {Res, {TrustedCAs, [Cert|IntermediateCerts]}}
+ end;
+validate_function(_Cert, _Event, State) ->
+ %ct:log("~p:~p~nignoring event ~p~n", [?MODULE,?LINE, _Event]),
+ {valid, State}.
+
+%% @doc Given a certificate, find CRL distribution points for the given
+%% certificate, fetch, and attempt to validate each CRL through
+%% issuer_function/4.
+%%
+check_crl(Cert, State) ->
+ %% pull the CRL distribution point(s) out of the certificate, if any
+ ct:log("~p:~p~ncheck_crl(~n Cert=~p,~nState=~p~n)",[?MODULE,?LINE,Cert,State]),
+ case pubkey_cert:select_extension(
+ ?'id-ce-cRLDistributionPoints',
+ pubkey_cert:extensions_list(Cert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.extensions)) of
+ undefined ->
+ ct:log("~p:~p~nno CRL distribution points for ~p~n",
+ [?MODULE,?LINE, get_common_name(Cert)]),
+ %% fail; we can't validate if there's no CRL
+ no_crl;
+ CRLExtension ->
+ ct:log("~p:~p~nCRLExtension=~p)",[?MODULE,?LINE,CRLExtension]),
+ CRLDistPoints = CRLExtension#'Extension'.extnValue,
+ DPointsAndCRLs = lists:foldl(fun(Point, Acc) ->
+ %% try to read the CRL over http or from a
+ %% local file
+ case fetch_point(Point) of
+ not_available ->
+ ct:log("~p:~p~nfetch_point returned~n~p~n)",[?MODULE,?LINE,not_available]),
+ Acc;
+ Res ->
+ ct:log("~p:~p~nfetch_point returned~n~p~n)",[?MODULE,?LINE,Res]),
+ [{Point, Res} | Acc]
+ end
+ end, [], CRLDistPoints),
+ public_key:pkix_crls_validate(Cert,
+ DPointsAndCRLs,
+ [{issuer_fun,
+ {fun issuer_function/4, State}}])
+ end.
+
+%% @doc Given a list of distribution points for CRLs, certificates and
+%% both trusted and intermediary certificates, attempt to build and
+%% authority chain back via build_chain to verify that it is valid.
+%%
+issuer_function(_DP, CRL, _Issuer, {TrustedCAs, IntermediateCerts}) ->
+ %% XXX the 'Issuer' we get passed here is the AuthorityKeyIdentifier,
+ %% which we are not currently smart enough to understand
+ %% Read the CA certs out of the file
+ ct:log("~p:~p~nissuer_function(~nCRL=~p,~nLast param=~p)",[?MODULE,?LINE,CRL, {TrustedCAs, IntermediateCerts}]),
+ Certs = [public_key:pkix_decode_cert(DER, otp) || DER <- TrustedCAs],
+ %% get the real issuer out of the CRL
+ Issuer = public_key:pkix_normalize_name(
+ pubkey_cert_records:transform(
+ CRL#'CertificateList'.tbsCertList#'TBSCertList'.issuer, decode)),
+ %% assume certificates are ordered from root to tip
+ case find_issuer(Issuer, IntermediateCerts ++ Certs) of
+ undefined ->
+ ct:log("~p:~p~nunable to find certificate matching CRL issuer ~p~n",
+ [?MODULE,?LINE, Issuer]),
+ error;
+ IssuerCert ->
+ ct:log("~p:~p~nIssuerCert=~p~n)",[?MODULE,?LINE,IssuerCert]),
+ case build_chain({public_key:pkix_encode('OTPCertificate',
+ IssuerCert,
+ otp),
+ IssuerCert}, IntermediateCerts, Certs, []) of
+ undefined ->
+ error;
+ {OTPCert, Path} ->
+ {ok, OTPCert, Path}
+ end
+ end.
+
+%% @doc Attempt to build authority chain back using intermediary
+%% certificates, falling back on trusted certificates if the
+%% intermediary chain of certificates does not fully extend to the
+%% root.
+%%
+%% Returns: {RootCA :: #OTPCertificate{}, Chain :: [der_encoded()]}
+%%
+build_chain({DER, Cert}, IntCerts, TrustedCerts, Acc) ->
+ %% check if this cert is self-signed, if it is, we've reached the
+ %% root of the chain
+ Issuer = public_key:pkix_normalize_name(
+ Cert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.issuer),
+ Subject = public_key:pkix_normalize_name(
+ Cert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject),
+ case Issuer == Subject of
+ true ->
+ case find_issuer(Issuer, TrustedCerts) of
+ undefined ->
+ ct:log("~p:~p~nself-signed certificate is NOT trusted~n",[?MODULE,?LINE]),
+ undefined;
+ TrustedCert ->
+ %% return the cert from the trusted list, to prevent
+ %% issuer spoofing
+ {TrustedCert,
+ [public_key:pkix_encode(
+ 'OTPCertificate', TrustedCert, otp)|Acc]}
+ end;
+ false ->
+ Match = lists:foldl(
+ fun(C, undefined) ->
+ S = public_key:pkix_normalize_name(C#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject),
+ %% compare the subject to the current issuer
+ case Issuer == S of
+ true ->
+ %% we've found our man
+ {public_key:pkix_encode('OTPCertificate', C, otp), C};
+ false ->
+ undefined
+ end;
+ (_E, A) ->
+ %% already matched
+ A
+ end, undefined, IntCerts),
+ case Match of
+ undefined when IntCerts /= TrustedCerts ->
+ %% continue the chain by using the trusted CAs
+ ct:log("~p:~p~nRan out of intermediate certs, switching to trusted certs~n",[?MODULE,?LINE]),
+ build_chain({DER, Cert}, TrustedCerts, TrustedCerts, Acc);
+ undefined ->
+ ct:log("Can't construct chain of trust beyond ~p~n",
+ [?MODULE,?LINE, get_common_name(Cert)]),
+ %% can't find the current cert's issuer
+ undefined;
+ Match ->
+ build_chain(Match, IntCerts, TrustedCerts, [DER|Acc])
+ end
+ end.
+
+%% @doc Given a certificate and a list of trusted or intermediary
+%% certificates, attempt to find a match in the list or bail with
+%% undefined.
+find_issuer(Issuer, Certs) ->
+ lists:foldl(
+ fun(OTPCert, undefined) ->
+ %% check if this certificate matches the issuer
+ Normal = public_key:pkix_normalize_name(
+ OTPCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject),
+ case Normal == Issuer of
+ true ->
+ OTPCert;
+ false ->
+ undefined
+ end;
+ (_E, Acc) ->
+ %% already found a match
+ Acc
+ end, undefined, Certs).
+
+%% @doc Find distribution points for a given CRL and then attempt to
+%% fetch the CRL from the first available.
+fetch_point(#'DistributionPoint'{distributionPoint={fullName, Names}}) ->
+ Decoded = [{NameType,
+ pubkey_cert_records:transform(Name, decode)}
+ || {NameType, Name} <- Names],
+ ct:log("~p:~p~ncall fetch(~nDecoded=~p~n)",[?MODULE,?LINE,Decoded]),
+ fetch(Decoded).
+
+%% @doc Given a list of locations to retrieve a CRL from, attempt to
+%% retrieve either from a file or http resource and bail as soon as
+%% it can be found.
+%%
+%% Currently, only hand a armored PEM or DER encoded file, with
+%% defaulting to DER.
+%%
+fetch([]) ->
+ not_available;
+fetch([{uniformResourceIdentifier, "http"++_=URL}|Rest]) ->
+ ct:log("~p:~p~ngetting CRL from ~p~n", [?MODULE,?LINE, URL]),
+ case httpc:request(get, {URL, []}, [], [{body_format, binary}]) of
+ {ok, {_Status, _Headers, Body}} ->
+ case Body of
+ <<"-----BEGIN", _/binary>> ->
+ ct:log("~p:~p~npublic_key:pem_decode,~nBody=~p~n)",[?MODULE,?LINE,Body]),
+ [{'CertificateList',
+ DER, _}=CertList] = public_key:pem_decode(Body),
+ ct:log("~p:~p~npublic_key:pem_entry_decode,~nCertList=~p~n)",[?MODULE,?LINE,CertList]),
+ {DER, public_key:pem_entry_decode(CertList)};
+ _ ->
+ ct:log("~p:~p~npublic_key:pem_entry_decode,~nBody=~p~n)",[?MODULE,?LINE,{'CertificateList', Body, not_encrypted}]),
+ %% assume DER encoded
+ try
+ public_key:pem_entry_decode({'CertificateList', Body, not_encrypted})
+ of
+ CertList -> {Body, CertList}
+ catch
+ _C:_E ->
+ ct:log("~p:~p~nfailed DER assumption~nRest=~p", [?MODULE,?LINE,Rest]),
+ fetch(Rest)
+ end
+ end;
+ {error, _Reason} ->
+ ct:log("~p:~p~nfailed to get CRL ~p~n", [?MODULE,?LINE, _Reason]),
+ fetch(Rest);
+ Other ->
+ ct:log("~p:~p~nreally failed to get CRL ~p~n", [?MODULE,?LINE, Other]),
+ fetch(Rest)
+ end;
+fetch([Loc|Rest]) ->
+ %% unsupported CRL location
+ ct:log("~p:~p~nunable to fetch CRL from unsupported location ~p~n",
+ [?MODULE,?LINE, Loc]),
+ fetch(Rest).
+
+%% get the common name attribute out of an OTPCertificate record
+get_common_name(OTPCert) ->
+ %% You'd think there'd be an easier way than this giant mess, but I
+ %% couldn't find one.
+ {rdnSequence, Subject} = OTPCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subject,
+ case [Attribute#'AttributeTypeAndValue'.value || [Attribute] <- Subject,
+ Attribute#'AttributeTypeAndValue'.type == ?'id-at-commonName'] of
+ [Att] ->
+ case Att of
+ {teletexString, Str} -> Str;
+ {printableString, Str} -> Str;
+ {utf8String, Bin} -> binary_to_list(Bin)
+ end;
+ _ ->
+ unknown
+ end.
+
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 7e8e8d2611..5f36842f9e 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-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,7 @@
-include_lib("common_test/include/ct.hrl").
-include("ssl_internal.hrl").
-include("tls_handshake.hrl").
+-include_lib("public_key/include/public_key.hrl").
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -34,8 +35,10 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() -> [decode_hello_handshake,
decode_single_hello_extension_correctly,
+ decode_supported_elliptic_curves_hello_extension_correctly,
decode_unknown_hello_extension_correctly,
- encode_single_hello_sni_extension_correctly].
+ encode_single_hello_sni_extension_correctly,
+ select_proper_tls_1_2_rsa_default_hashsign].
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
@@ -67,6 +70,17 @@ decode_single_hello_extension_correctly(_Config) ->
#renegotiation_info{renegotiated_connection = <<0>>}
= Extensions#hello_extensions.renegotiation_info.
+decode_supported_elliptic_curves_hello_extension_correctly(_Config) ->
+ % List of supported and unsupported curves (RFC4492:S5.1.1)
+ ClientEllipticCurves = [0, tls_v1:oid_to_enum(?sect233k1), 37, tls_v1:oid_to_enum(?sect193r2), 16#badc],
+ % Construct extension binary - modified version of ssl_handshake:encode_hello_extensions([#elliptic_curves{}], _)
+ EllipticCurveList = << <<X:16>> || X <- ClientEllipticCurves>>,
+ ListLen = byte_size(EllipticCurveList),
+ Len = ListLen + 2,
+ Extension = <<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), ?UINT16(ListLen), EllipticCurveList/binary>>,
+ % after decoding we should see only valid curves
+ #hello_extensions{elliptic_curves = DecodedCurves} = ssl_handshake:decode_hello_extensions(Extension),
+ #elliptic_curves{elliptic_curve_list = [?sect233k1, ?sect193r2]} = DecodedCurves.
decode_unknown_hello_extension_correctly(_Config) ->
FourByteUnknown = <<16#CA,16#FE, ?UINT16(4), 3, 0, 1, 2>>,
@@ -83,3 +97,11 @@ encode_single_hello_sni_extension_correctly(_Config) ->
HelloExt = <<ExtSize:16/unsigned-big-integer, SNI/binary>>,
Encoded = ssl_handshake:encode_hello_extensions(Exts),
HelloExt = Encoded.
+
+select_proper_tls_1_2_rsa_default_hashsign(_Config) ->
+ % RFC 5246 section 7.4.1.4.1 tells to use {sha1,rsa} as default signature_algorithm for RSA key exchanges
+ {sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,3}),
+ % Older versions use MD5/SHA1 combination
+ {md5sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,2}),
+ {md5sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,0}).
+
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 7ed9adfcd9..150b5037d7 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -60,14 +60,23 @@ run_server(Opts) ->
Options = proplists:get_value(options, Opts),
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
- ct:log("ssl:listen(~p, ~p)~n", [Port, Options]),
+ ct:log("~p:~p~nssl:listen(~p, ~p)~n", [?MODULE,?LINE, Port, Options]),
{ok, ListenSocket} = rpc:call(Node, Transport, listen, [Port, Options]),
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
run_server(ListenSocket, Opts).
run_server(ListenSocket, Opts) ->
- do_run_server(ListenSocket, connect(ListenSocket, Opts), Opts).
+ Accepters = proplists:get_value(accepters, Opts, 1),
+ run_server(ListenSocket, Opts, Accepters).
+
+run_server(ListenSocket, Opts, 1) ->
+ do_run_server(ListenSocket, connect(ListenSocket, Opts), Opts);
+run_server(ListenSocket, Opts, N) ->
+ Pid = proplists:get_value(from, Opts),
+ Server = spawn(?MODULE, run_server, [ListenSocket, Opts, 1]),
+ Pid ! {accepter, N, Server},
+ run_server(ListenSocket, Opts, N-1).
do_run_server(_, {error, timeout} = Result, Opts) ->
Pid = proplists:get_value(from, Opts),
@@ -78,13 +87,13 @@ do_run_server(ListenSocket, AcceptSocket, Opts) ->
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
- ct:log("Server: apply(~p,~p,~p)~n",
- [Module, Function, [AcceptSocket | Args]]),
+ ct:log("~p:~p~nServer: apply(~p,~p,~p)~n",
+ [?MODULE,?LINE, Module, Function, [AcceptSocket | Args]]),
case rpc:call(Node, Module, Function, [AcceptSocket | Args]) of
no_result_msg ->
ok;
Msg ->
- ct:log("Server Msg: ~p ~n", [Msg]),
+ ct:log("~p:~p~nServer Msg: ~p ~n", [?MODULE,?LINE, Msg]),
Pid ! {self(), Msg}
end,
receive
@@ -93,10 +102,10 @@ do_run_server(ListenSocket, AcceptSocket, Opts) ->
{listen, MFA} ->
run_server(ListenSocket, [MFA | proplists:delete(mfa, Opts)]);
close ->
- ct:log("Server closing ~p ~n", [self()]),
+ ct:log("~p:~p~nServer closing ~p ~n", [?MODULE,?LINE, self()]),
Result = rpc:call(Node, Transport, close, [AcceptSocket], 500),
Result1 = rpc:call(Node, Transport, close, [ListenSocket], 500),
- ct:log("Result ~p : ~p ~n", [Result, Result1]);
+ ct:log("~p:~p~nResult ~p : ~p ~n", [?MODULE,?LINE, Result, Result1]);
{ssl_closed, _} ->
ok
end.
@@ -106,7 +115,8 @@ connect(#sslsocket{} = ListenSocket, Opts) ->
Node = proplists:get_value(node, Opts),
ReconnectTimes = proplists:get_value(reconnect_times, Opts, 0),
Timeout = proplists:get_value(timeout, Opts, infinity),
- AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy, Timeout),
+ SslOpts = proplists:get_value(ssl_extra_opts, Opts, []),
+ AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy, Timeout, SslOpts),
case ReconnectTimes of
0 ->
AcceptSocket;
@@ -116,27 +126,35 @@ connect(#sslsocket{} = ListenSocket, Opts) ->
end;
connect(ListenSocket, Opts) ->
Node = proplists:get_value(node, Opts),
- ct:log("gen_tcp:accept(~p)~n", [ListenSocket]),
+ ct:log("~p:~p~ngen_tcp:accept(~p)~n", [?MODULE,?LINE, ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept,
[ListenSocket]),
AcceptSocket.
-connect(_, _, 0, AcceptSocket, _) ->
+connect(_, _, 0, AcceptSocket, _, _) ->
AcceptSocket;
-connect(ListenSocket, Node, N, _, Timeout) ->
+
+connect(ListenSocket, Node, N, _, Timeout, []) ->
ct:log("ssl:transport_accept(~p)~n", [ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
[ListenSocket]),
- ct:log("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]),
+ ct:log("~p:~p~nssl:ssl_accept(~p, ~p)~n", [?MODULE,?LINE, AcceptSocket, Timeout]),
case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of
ok ->
- connect(ListenSocket, Node, N-1, AcceptSocket, Timeout);
+ connect(ListenSocket, Node, N-1, AcceptSocket, Timeout, []);
Result ->
+ ct:log("~p:~p~nssl:ssl_accept@~p ret ~p",[?MODULE,?LINE, Node,Result]),
Result
- end.
+ end;
+connect(ListenSocket, Node, _, _, Timeout, Opts) ->
+ ct:log("ssl:transport_accept(~p)~n", [ListenSocket]),
+ {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
+ [ListenSocket]),
+ ct:log("ssl:ssl_accept(~p,~p, ~p)~n", [AcceptSocket, Opts, Timeout]),
+ rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Opts, Timeout]),
+ AcceptSocket.
-
remove_close_msg(0) ->
ok;
remove_close_msg(ReconnectTimes) ->
@@ -148,11 +166,13 @@ remove_close_msg(ReconnectTimes) ->
start_client(Args) ->
Result = spawn_link(?MODULE, run_client_init, [lists:delete(return_socket, Args)]),
receive
- { connected, Socket } ->
- case lists:member(return_socket, Args) of
- true -> { Result, Socket };
- false -> Result
- end
+ {connected, Socket} ->
+ case lists:member(return_socket, Args) of
+ true -> {Result, Socket};
+ false -> Result
+ end;
+ {connect_failed, Reason} ->
+ {connect_failed, Reason}
end.
run_client_init(Opts) ->
@@ -166,27 +186,27 @@ run_client(Opts) ->
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
Options = proplists:get_value(options, Opts),
- ct:log("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
+ ct:log("~p:~p~n~p:connect(~p, ~p)@~p~n", [?MODULE,?LINE, Transport, Host, Port, Node]),
case rpc:call(Node, Transport, connect, [Host, Port, Options]) of
{ok, Socket} ->
- Pid ! { connected, Socket },
- ct:log("Client: connected~n", []),
+ Pid ! {connected, Socket},
+ ct:log("~p:~p~nClient: connected~n", [?MODULE,?LINE]),
%% In special cases we want to know the client port, it will
%% be indicated by sending {port, 0} in options list!
send_selected_port(Pid, proplists:get_value(port, Options), Socket),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
- ct:log("Client: apply(~p,~p,~p)~n",
- [Module, Function, [Socket | Args]]),
+ ct:log("~p:~p~nClient: apply(~p,~p,~p)~n",
+ [?MODULE,?LINE, Module, Function, [Socket | Args]]),
case rpc:call(Node, Module, Function, [Socket | Args]) of
no_result_msg ->
ok;
Msg ->
- ct:log("Client Msg: ~p ~n", [Msg]),
+ ct:log("~p:~p~nClient Msg: ~p ~n", [?MODULE,?LINE, Msg]),
Pid ! {self(), Msg}
end,
receive
close ->
- ct:log("Client closing~n", []),
+ ct:log("~p:~p~nClient closing~n", [?MODULE,?LINE]),
rpc:call(Node, Transport, close, [Socket]);
{ssl_closed, Socket} ->
ok;
@@ -196,50 +216,42 @@ run_client(Opts) ->
{error, econnrefused = Reason} ->
case get(retries) of
N when N < 5 ->
+ ct:log("~p:~p~neconnrefused retries=~p sleep ~p",[?MODULE,?LINE, N,?SLEEP]),
put(retries, N+1),
ct:sleep(?SLEEP),
run_client(Opts);
_ ->
- ct:log("Client faild several times: connection failed: ~p ~n", [Reason]),
+ ct:log("~p:~p~nClient faild several times: connection failed: ~p ~n", [?MODULE,?LINE, Reason]),
Pid ! {self(), {error, Reason}}
end;
{error, Reason} ->
- ct:log("Client: connection failed: ~p ~n", [Reason]),
- Pid ! {self(), {error, Reason}}
+ ct:log("~p:~p~nClient: connection failed: ~p ~n", [?MODULE,?LINE, Reason]),
+ Pid ! {connect_failed, Reason};
+ {badrpc,BadRPC} ->
+ ct:log("~p:~p~nBad rpc: ~p",[?MODULE,?LINE, BadRPC]),
+ Pid ! {connect_failed, {badrpc,BadRPC}}
end.
close(Pid) ->
- ct:log("Close ~p ~n", [Pid]),
+ ct:log("~p:~p~nClose ~p ~n", [?MODULE,?LINE, Pid]),
Monitor = erlang:monitor(process, Pid),
Pid ! close,
receive
{'DOWN', Monitor, process, Pid, Reason} ->
erlang:demonitor(Monitor),
- ct:log("Pid: ~p down due to:~p ~n", [Pid, Reason])
+ ct:log("~p:~p~nPid: ~p down due to:~p ~n", [?MODULE,?LINE, Pid, Reason])
end.
check_result(Server, ServerMsg, Client, ClientMsg) ->
receive
- {Server, ServerMsg} ->
- receive
- {Client, ClientMsg} ->
- ok;
- Unexpected ->
- Reason = {{expected, {Client, ClientMsg}},
- {got, Unexpected}},
- ct:fail(Reason)
- end;
- {Client, ClientMsg} ->
- receive
- {Server, ServerMsg} ->
- ok;
- Unexpected ->
- Reason = {{expected, {Server, ClientMsg}},
- {got, Unexpected}},
- ct:fail(Reason)
- end;
+ {Server, ServerMsg} ->
+ check_result(Client, ClientMsg);
+
+ {Client, ClientMsg} ->
+ check_result(Server, ServerMsg);
+
{Port, {data,Debug}} when is_port(Port) ->
- io:format("openssl ~s~n",[Debug]),
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
check_result(Server, ServerMsg, Client, ClientMsg);
Unexpected ->
@@ -253,7 +265,7 @@ check_result(Pid, Msg) ->
{Pid, Msg} ->
ok;
{Port, {data,Debug}} when is_port(Port) ->
- io:format("openssl ~s~n",[Debug]),
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
check_result(Pid,Msg);
Unexpected ->
Reason = {{expected, {Pid, Msg}},
@@ -278,19 +290,28 @@ wait_for_result(Server, ServerMsg, Client, ClientMsg) ->
%% Unexpected
end;
{Port, {data,Debug}} when is_port(Port) ->
- io:format("openssl ~s~n",[Debug]),
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
wait_for_result(Server, ServerMsg, Client, ClientMsg)
%% Unexpected ->
%% Unexpected
end.
-
+check_ok([]) ->
+ ok;
+check_ok(Pids) ->
+ receive
+ {Pid, ok} ->
+ check_ok(lists:delete(Pid, Pids));
+ Other ->
+ ct:fail({expected, {"pid()", ok}, got, Other})
+ end.
+
wait_for_result(Pid, Msg) ->
receive
{Pid, Msg} ->
ok;
{Port, {data,Debug}} when is_port(Port) ->
- io:format("openssl ~s~n",[Debug]),
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
wait_for_result(Pid,Msg)
%% Unexpected ->
%% Unexpected
@@ -515,33 +536,33 @@ run_upgrade_server(Opts) ->
SslOptions = proplists:get_value(ssl_options, Opts),
Pid = proplists:get_value(from, Opts),
- ct:log("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]),
+ ct:log("~p:~p~ngen_tcp:listen(~p, ~p)~n", [?MODULE,?LINE, Port, TcpOptions]),
{ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]),
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
- ct:log("gen_tcp:accept(~p)~n", [ListenSocket]),
+ ct:log("~p:~p~ngen_tcp:accept(~p)~n", [?MODULE,?LINE, ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]),
try
{ok, SslAcceptSocket} = case TimeOut of
infinity ->
- ct:log("ssl:ssl_accept(~p, ~p)~n",
- [AcceptSocket, SslOptions]),
+ ct:log("~p:~p~nssl:ssl_accept(~p, ~p)~n",
+ [?MODULE,?LINE, AcceptSocket, SslOptions]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions]);
_ ->
- ct:log("ssl:ssl_accept(~p, ~p, ~p)~n",
- [AcceptSocket, SslOptions, TimeOut]),
+ ct:log("~p:~p~nssl:ssl_accept(~p, ~p, ~p)~n",
+ [?MODULE,?LINE, AcceptSocket, SslOptions, TimeOut]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions, TimeOut])
end,
{Module, Function, Args} = proplists:get_value(mfa, Opts),
Msg = rpc:call(Node, Module, Function, [SslAcceptSocket | Args]),
- ct:log("Upgrade Server Msg: ~p ~n", [Msg]),
+ ct:log("~p:~p~nUpgrade Server Msg: ~p ~n", [?MODULE,?LINE, Msg]),
Pid ! {self(), Msg},
receive
close ->
- ct:log("Upgrade Server closing~n", []),
+ ct:log("~p:~p~nUpgrade Server closing~n", [?MODULE,?LINE]),
rpc:call(Node, ssl, close, [SslAcceptSocket])
end
catch error:{badmatch, Error} ->
@@ -559,24 +580,24 @@ run_upgrade_client(Opts) ->
TcpOptions = proplists:get_value(tcp_options, Opts),
SslOptions = proplists:get_value(ssl_options, Opts),
- ct:log("gen_tcp:connect(~p, ~p, ~p)~n",
- [Host, Port, TcpOptions]),
+ ct:log("~p:~p~ngen_tcp:connect(~p, ~p, ~p)~n",
+ [?MODULE,?LINE, Host, Port, TcpOptions]),
{ok, Socket} = rpc:call(Node, gen_tcp, connect, [Host, Port, TcpOptions]),
send_selected_port(Pid, Port, Socket),
- ct:log("ssl:connect(~p, ~p)~n", [Socket, SslOptions]),
+ ct:log("~p:~p~nssl:connect(~p, ~p)~n", [?MODULE,?LINE, Socket, SslOptions]),
{ok, SslSocket} = rpc:call(Node, ssl, connect, [Socket, SslOptions]),
{Module, Function, Args} = proplists:get_value(mfa, Opts),
- ct:log("apply(~p, ~p, ~p)~n",
- [Module, Function, [SslSocket | Args]]),
+ ct:log("~p:~p~napply(~p, ~p, ~p)~n",
+ [?MODULE,?LINE, Module, Function, [SslSocket | Args]]),
Msg = rpc:call(Node, Module, Function, [SslSocket | Args]),
- ct:log("Upgrade Client Msg: ~p ~n", [Msg]),
+ ct:log("~p:~p~nUpgrade Client Msg: ~p ~n", [?MODULE,?LINE, Msg]),
Pid ! {self(), Msg},
receive
close ->
- ct:log("Upgrade Client closing~n", []),
+ ct:log("~p:~p~nUpgrade Client closing~n", [?MODULE,?LINE]),
rpc:call(Node, ssl, close, [SslSocket])
end.
@@ -595,21 +616,21 @@ run_upgrade_server_error(Opts) ->
SslOptions = proplists:get_value(ssl_options, Opts),
Pid = proplists:get_value(from, Opts),
- ct:log("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]),
+ ct:log("~p:~p~ngen_tcp:listen(~p, ~p)~n", [?MODULE,?LINE, Port, TcpOptions]),
{ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]),
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
- ct:log("gen_tcp:accept(~p)~n", [ListenSocket]),
+ ct:log("~p:~p~ngen_tcp:accept(~p)~n", [?MODULE,?LINE, ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]),
Error = case TimeOut of
infinity ->
- ct:log("ssl:ssl_accept(~p, ~p)~n",
- [AcceptSocket, SslOptions]),
+ ct:log("~p:~p~nssl:ssl_accept(~p, ~p)~n",
+ [?MODULE,?LINE, AcceptSocket, SslOptions]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions]);
_ ->
- ct:log("ssl:ssl_accept(~p, ~p, ~p)~n",
- [AcceptSocket, SslOptions, TimeOut]),
+ ct:log("~p:~p~nssl:ssl_accept(~p, ~p, ~p)~n",
+ [?MODULE,?LINE, AcceptSocket, SslOptions, TimeOut]),
rpc:call(Node, ssl, ssl_accept,
[AcceptSocket, SslOptions, TimeOut])
end,
@@ -628,26 +649,26 @@ run_server_error(Opts) ->
Options = proplists:get_value(options, Opts),
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
- ct:log("ssl:listen(~p, ~p)~n", [Port, Options]),
+ ct:log("~p:~p~nssl:listen(~p, ~p)~n", [?MODULE,?LINE, Port, Options]),
case rpc:call(Node, Transport, listen, [Port, Options]) of
{ok, #sslsocket{} = ListenSocket} ->
%% To make sure error_client will
%% get {error, closed} and not {error, connection_refused}
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
- ct:log("ssl:transport_accept(~p)~n", [ListenSocket]),
+ ct:log("~p:~p~nssl:transport_accept(~p)~n", [?MODULE,?LINE, ListenSocket]),
case rpc:call(Node, Transport, transport_accept, [ListenSocket]) of
{error, _} = Error ->
Pid ! {self(), Error};
{ok, AcceptSocket} ->
- ct:log("ssl:ssl_accept(~p)~n", [AcceptSocket]),
+ ct:log("~p:~p~nssl:ssl_accept(~p)~n", [?MODULE,?LINE, AcceptSocket]),
Error = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]),
Pid ! {self(), Error}
end;
{ok, ListenSocket} ->
Pid ! {listen, up},
send_selected_port(Pid, Port, ListenSocket),
- ct:log("~p:accept(~p)~n", [Transport, ListenSocket]),
+ ct:log("~p:~p~n~p:accept(~p)~n", [?MODULE,?LINE, Transport, ListenSocket]),
case rpc:call(Node, Transport, accept, [ListenSocket]) of
{error, _} = Error ->
Pid ! {self(), Error}
@@ -669,10 +690,21 @@ run_client_error(Opts) ->
Pid = proplists:get_value(from, Opts),
Transport = proplists:get_value(transport, Opts, ssl),
Options = proplists:get_value(options, Opts),
- ct:log("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
+ ct:log("~p:~p~nssl:connect(~p, ~p, ~p)~n", [?MODULE,?LINE, Host, Port, Options]),
Error = rpc:call(Node, Transport, connect, [Host, Port, Options]),
Pid ! {self(), Error}.
+accepters(N) ->
+ accepters([], N).
+
+accepters(Acc, 0) ->
+ Acc;
+accepters(Acc, N) ->
+ receive
+ {accepter, _, Server} ->
+ accepters([Server| Acc], N-1)
+ end.
+
inet_port(Pid) when is_pid(Pid)->
receive
{Pid, {port, Port}} ->
@@ -840,25 +872,34 @@ psk_suites() ->
{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_256_cbc, sha},
+ {rsa_psk, aes_128_cbc, sha256},
+ {rsa_psk, aes_256_cbc, sha384}
+],
ssl_cipher:filter_suites(Suites).
psk_anon_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}].
+ 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}],
+ ssl_cipher:filter_suites(Suites).
srp_suites() ->
Suites =
@@ -871,9 +912,11 @@ srp_suites() ->
ssl_cipher:filter_suites(Suites).
srp_anon_suites() ->
- [{srp_anon, '3des_ede_cbc', sha},
- {srp_anon, aes_128_cbc, sha},
- {srp_anon, aes_256_cbc, sha}].
+ Suites =
+ [{srp_anon, '3des_ede_cbc', sha},
+ {srp_anon, aes_128_cbc, sha},
+ {srp_anon, aes_256_cbc, sha}],
+ ssl_cipher:filter_suites(Suites).
srp_dss_suites() ->
Suites =
@@ -892,7 +935,7 @@ der_to_pem(File, Entries) ->
cipher_result(Socket, Result) ->
Result = ssl:connection_info(Socket),
- ct:log("Successfull connect: ~p~n", [Result]),
+ ct:log("~p:~p~nSuccessfull connect: ~p~n", [?MODULE,?LINE, Result]),
%% Importante to send two packets here
%% to properly test "cipher state" handling
ssl:send(Socket, "Hello\n"),
@@ -1061,10 +1104,13 @@ check_sane_openssl_version(Version) ->
true
end.
+enough_openssl_crl_support("OpenSSL 0." ++ _) -> false;
+enough_openssl_crl_support(_) -> true.
+
wait_for_openssl_server() ->
receive
{Port, {data, Debug}} when is_port(Port) ->
- ct:log("openssl ~s~n",[Debug]),
+ ct:log("~p:~p~nopenssl ~s~n",[?MODULE,?LINE, Debug]),
%% openssl has started make sure
%% it will be in accept. Parsing
%% output is too error prone. (Even
@@ -1080,3 +1126,13 @@ version_flag('tlsv1.2') ->
" -tls1_2 ";
version_flag(sslv3) ->
" -ssl3 ".
+
+filter_suites(Ciphers0) ->
+ Version = tls_record:highest_protocol_version([]),
+ Supported0 = ssl_cipher:suites(Version)
+ ++ ssl_cipher:anonymous_suites()
+ ++ ssl_cipher:psk_suites(Version)
+ ++ ssl_cipher:srp_suites(),
+ Supported1 = ssl_cipher:filter_suites(Supported0),
+ Supported2 = [ssl:suite_definition(S) || S <- Supported1],
+ [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported2)].
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index a7361755e5..d36e441c7a 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -1341,7 +1341,7 @@ check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1.1';
{skip, "Known renegotiation bug in OpenSSL"};
"OpenSSL 1.0.1a" ++ _ ->
{skip, "Known renegotiation bug in OpenSSL"};
- "OpenSSL 1.0.1" ++ _ ->
+ "OpenSSL 1.0.1 " ++ _ ->
{skip, "Known renegotiation bug in OpenSSL"};
_ ->
check_sane_openssl_renegotaite(Config)
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index e08f5dff78..004cacf7fc 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.3.4
+SSL_VSN = 5.3.5
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile
index 6f1e61e70c..ff77c3eea0 100644
--- a/lib/stdlib/doc/src/Makefile
+++ b/lib/stdlib/doc/src/Makefile
@@ -71,6 +71,7 @@ XML_REF3_FILES = \
lib.xml \
lists.xml \
log_mf_h.xml \
+ maps.xml \
math.xml \
ms_transform.xml \
orddict.xml \
diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml
index cf33530395..452341f7d2 100644
--- a/lib/stdlib/doc/src/epp.xml
+++ b/lib/stdlib/doc/src/epp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -46,8 +46,10 @@
valid encodings are <c>Latin-1</c> and <c>UTF-8</c> where the
case of the characters can be chosen freely. Examples:</p>
<pre>
-%% coding: utf-8
-%% For this file we have chosen encoding = Latin-1
+%% coding: utf-8</pre>
+ <pre>
+%% For this file we have chosen encoding = Latin-1</pre>
+ <pre>
%% -*- coding: latin-1 -*-</pre>
</description>
<datatypes>
@@ -64,11 +66,29 @@
</datatypes>
<funcs>
<func>
+ <name name="open" arity="1"/>
+ <fsummary>Open a file for preprocessing</fsummary>
+ <desc>
+ <p>Opens a file for preprocessing.</p>
+ <p>If <c>extra</c> is given in
+ <c><anno>Options</anno></c>, the return value will be
+ <c>{ok, <anno>Epp</anno>, <anno>Extra</anno>}</c> instead
+ of <c>{ok, <anno>Epp</anno>}</c>.</p>
+ </desc>
+ </func>
+ <func>
<name name="open" arity="2"/>
+ <fsummary>Open a file for preprocessing</fsummary>
+ <desc>
+ <p>Equivalent to <c>epp:open([{name, FileName}, {includes, IncludePath}])</c>.</p>
+ </desc>
+ </func>
+ <func>
<name name="open" arity="3"/>
<fsummary>Open a file for preprocessing</fsummary>
<desc>
- <p>Opens a file for preprocessing.</p>
+ <p>Equivalent to <c>epp:open([{name, FileName}, {includes, IncludePath},
+ {macros, PredefMacros}])</c>.</p>
</desc>
</func>
<func>
@@ -89,12 +109,24 @@
</desc>
</func>
<func>
- <name name="parse_file" arity="3"/>
+ <name name="parse_file" arity="2"/>
<fsummary>Preprocess and parse an Erlang source file</fsummary>
<desc>
<p>Preprocesses and parses an Erlang source file.
- Note that the tuple <c>{eof, <anno>Line</anno>}</c> returned at end-of-file is
- included as a "form".</p>
+ Note that the tuple <c>{eof, <anno>Line</anno>}</c> returned
+ at end-of-file is included as a "form".</p>
+ <p>If <c>extra</c> is given in
+ <c><anno>Options</anno></c>, the return value will be
+ <c>{ok, [<anno>Form</anno>], <anno>Extra</anno>}</c> instead
+ of <c>{ok, [<anno>Form</anno>]}</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="parse_file" arity="3"/>
+ <fsummary>Preprocess and parse an Erlang source file</fsummary>
+ <desc>
+ <p>Equivalent to <c>epp:parse_file(FileName, [{includes, IncludePath},
+ {macros, PredefMacros}])</c>.</p>
</desc>
</func>
<func>
@@ -111,7 +143,7 @@
<p>Returns a string representation of an encoding. The string
is recognized by <c>read_encoding/1,2</c>,
<c>read_encoding_from_binary/1,2</c>, and
- <c>set_encoding/1</c> as a valid encoding.</p>
+ <c>set_encoding/1,2</c> as a valid encoding.</p>
</desc>
</func>
<func>
@@ -157,6 +189,22 @@
</desc>
</func>
<func>
+ <name name="set_encoding" arity="2"/>
+ <fsummary>Read and set the encoding of an IO device</fsummary>
+ <desc>
+ <p>Reads the <seealso marker="#encoding">encoding</seealso> from
+ an IO device and sets the encoding of the device
+ accordingly. The position of the IO device referenced by
+ <c><anno>File</anno></c> is not affected. If no valid
+ encoding can be read from the IO device the encoding of the
+ IO device is set to the
+ <seealso marker="#encoding">encoding</seealso> given by
+ <c><anno>Default</anno></c>.</p>
+ <p>Returns the read encoding, or <c>none</c> if no valid
+ encoding was found.</p>
+ </desc>
+ </func>
+ <func>
<name name="format_error" arity="1"/>
<fsummary>Format an error descriptor</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/erl_parse.xml b/lib/stdlib/doc/src/erl_parse.xml
index 2d5aff3c6c..cf0bff48cd 100644
--- a/lib/stdlib/doc/src/erl_parse.xml
+++ b/lib/stdlib/doc/src/erl_parse.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -173,6 +173,7 @@
</func>
<func>
<name name="abstract" arity="2"/>
+ <type name="encoding_func"/>
<fsummary>Convert an Erlang term into an abstract form</fsummary>
<desc>
<p>Converts the Erlang data structure <c><anno>Data</anno></c> into an
@@ -183,7 +184,12 @@
selecting which integer lists will be considered
as strings. The default is to use the encoding returned by
<seealso marker="epp#default_encoding/0">
- <c>epp:default_encoding/0</c></seealso></p>
+ <c>epp:default_encoding/0</c></seealso>.
+ The value <c>none</c> means that no integer lists will be
+ considered as strings. The <c>encoding_func()</c> will be
+ called with one integer of a list at a time, and if it
+ returns <c>true</c> for every integer the list will be
+ considered a string.</p>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/doc/src/erl_tar.xml b/lib/stdlib/doc/src/erl_tar.xml
index f81e36f810..7f25f5b7bc 100644
--- a/lib/stdlib/doc/src/erl_tar.xml
+++ b/lib/stdlib/doc/src/erl_tar.xml
@@ -35,10 +35,11 @@
<modulesummary>Unix 'tar' utility for reading and writing tar archives</modulesummary>
<description>
<p>The <c>erl_tar</c> module archives and extract files to and from
- a tar file. The tar file format is the POSIX extended tar file format
- specified in IEEE Std 1003.1 and ISO/IEC&nbsp;9945-1. That is the same
- format as used by <c>tar</c> program on Solaris, but is not the same
- as used by the GNU tar program.</p>
+ a tar file. <c>erl_tar</c> supports the <c>ustar</c> format
+ (IEEE Std 1003.1 and ISO/IEC&nbsp;9945-1). All modern <c>tar</c>
+ programs (including GNU tar) can read this format. To ensure that
+ that GNU tar produces a tar file that <c>erl_tar</c> can read,
+ give the <c>--format=ustar</c> option to GNU tar.</p>
<p>By convention, the name of a tar file should end in "<c>.tar</c>".
To abide to the convention, you'll need to add "<c>.tar</c>" yourself
to the name.</p>
@@ -65,6 +66,20 @@
</description>
<section>
+ <title>UNICODE SUPPORT</title>
+ <p>If <seealso
+ marker="kernel:file#native_name_encoding/0">file:native_name_encoding/0</seealso>
+ returns <c>utf8</c>, path names will be encoded in UTF-8 when
+ creating tar files and path names will be assumed to be encoded in
+ UTF-8 when extracting tar files.</p>
+
+ <p>If <seealso
+ marker="kernel:file#native_name_encoding/0">file:native_name_encoding/0</seealso>
+ returns <c>latin1</c>, no translation of path names will be
+ done.</p>
+ </section>
+
+ <section>
<title>LIMITATIONS</title>
<p>For maximum compatibility, it is safe to archive files with names
up to 100 characters in length. Such tar files can generally be
@@ -112,8 +127,8 @@
<fsummary>Add a file to an open tar file</fsummary>
<type>
<v>TarDescriptor = term()</v>
- <v>FilenameOrBin = Filename()|binary()</v>
- <v>Filename = filename()()</v>
+ <v>FilenameOrBin = filename()|binary()</v>
+ <v>Filename = filename()</v>
<v>NameInArchive = filename()</v>
<v>Options = [Option]</v>
<v>Option = dereference|verbose</v>
diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml
new file mode 100644
index 0000000000..b37f7fd7fd
--- /dev/null
+++ b/lib/stdlib/doc/src/maps.xml
@@ -0,0 +1,338 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2013</year><year>2014</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+ </legalnotice>
+ <title>maps</title>
+ <prepared>Björn-Egil Dahlberg</prepared>
+ <docno>1</docno>
+ <date>2014-02-28</date>
+ <rev>A</rev>
+ </header>
+ <module>maps</module>
+ <modulesummary>Maps Processing Functions</modulesummary>
+ <description>
+ <p>This module contains functions for maps processing.</p>
+ </description>
+ <funcs>
+
+ <func>
+ <name name="find" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns a tuple <c>{ok, Value}</c> where <c><anno>Value</anno></c> is the value associated with <c><anno>Key</anno></c>,
+ or <c>error</c> if no value is associated with <c><anno>Key</anno></c> in <c><anno>Map</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"hi" => 42},
+ Key = "hi",
+ maps:find(Key,Map).
+{ok,42} </code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="fold" arity="3"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Calls <c>F(K, V, AccIn)</c> for every <c><anno>K</anno></c> to value <c><anno>V</anno></c>
+ association in <c><anno>Map</anno></c> in
+ arbitrary order. The function <c>fun F/3</c> must return a new accumulator
+ which is passed to the next successive call. <c>maps:fold/3</c> returns the final
+ value of the accumulator. The initial accumulator value <c><anno>Init</anno></c> is returned if
+ the map is empty.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Fun = fun(K,V,AccIn) when is_list(K) -> AccIn + V end,
+ Map = #{"k1" => 1, "k2" => 2, "k3" => 3},
+ maps:fold(Fun,0,Map).
+6</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="from_list" arity="1"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ The function takes a list of key-value tuples elements and builds a
+ map. The associations may be in any order and both keys and values in the
+ association may be of any term. If the same key appears more than once,
+ the latter (rightmost) value is used and the previous values are ignored.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> List = [{"a",ignored},{1337,"value two"},{42,value_three},{"a",1}],
+ maps:from_list(List).
+#{42 => value_three,1337 => "value two","a" => 1}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="get" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns the value <c><anno>Value</anno></c> associated with <c><anno>Key</anno></c> if
+ <c><anno>Map</anno></c> contains <c><anno>Key</anno></c>.
+ If no value is associated with <c><anno>Key</anno></c> then the call will
+ fail with an exception.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Key = 1337,
+ Map = #{42 => value_two,1337 => "value one","a" => 1},
+ maps:get(Key,Map).
+"value one"</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="get" arity="3"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns the value <c><anno>Value</anno></c> associated with <c><anno>Key</anno></c> if
+ <c><anno>Map</anno></c> contains <c><anno>Key</anno></c>.
+ If no value is associated with <c><anno>Key</anno></c> then returns <c><anno>Default</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{ key1 => val1, key2 => val2 }.
+#{key1 => val1,key2 => val2}
+> maps:get(key1, Map, "Default value").
+val1
+> maps:get(key3, Map, "Default value").
+"Default value"</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="is_key" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns <c>true</c> if map <c><anno>Map</anno></c> contains <c><anno>Key</anno></c> and returns
+ <c>false</c> if it does not contain the <c><anno>Key</anno></c>.
+ The function will fail with an exception if <c><anno>Map</anno></c> is not a Map.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"42" => value}.
+#{"42"> => value}
+> maps:is_key("42",Map).
+true
+> maps:is_key(value,Map).
+false</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="keys" arity="1"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns a complete list of keys, in arbitrary order, which resides within <c><anno>Map</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{42 => value_three,1337 => "value two","a" => 1},
+ maps:keys(Map).
+[42,1337,"a"]</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="map" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ The function produces a new map <c><anno>Map2</anno></c> by calling the function <c>fun F(K, V1)</c> for
+ every <c><anno>K</anno></c> to value <c><anno>V1</anno></c> association in <c><anno>Map1</anno></c> in arbitrary order.
+ The function <c>fun F/2</c> must return the value <c><anno>V2</anno></c> to be associated with key <c><anno>K</anno></c> for
+ the new map <c><anno>Map2</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Fun = fun(K,V1) when is_list(K) -> V1*2 end,
+ Map = #{"k1" => 1, "k2" => 2, "k3" => 3},
+ maps:map(Fun,Map).
+#{"k1" => 2,"k2" => 4,"k3" => 6}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="merge" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Merges two maps into a single map <c><anno>Map3</anno></c>. If two keys exists in both maps the
+ value in <c><anno>Map1</anno></c> will be superseded by the value in <c><anno>Map2</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map1 = #{a => "value_one", b => "value_two"},
+ Map2 = #{a => 1, c => 2},
+ maps:merge(Map1,Map2).
+#{a => 1,b => "value_two",c => 2}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="new" arity="0"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns a new empty map.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> maps:new().
+#{}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="put" arity="3"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Associates <c><anno>Key</anno></c> with value <c><anno>Value</anno></c> and inserts the association into map <c>Map2</c>.
+ If key <c><anno>Key</anno></c> already exists in map <c><anno>Map1</anno></c>, the old associated value is
+ replaced by value <c><anno>Value</anno></c>. The function returns a new map <c><anno>Map2</anno></c> containing the new association and
+ the old associations in <c><anno>Map1</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"a" => 1}.
+#{"a" => 1}
+> maps:put("a", 42, Map).
+#{"a" => 42}
+> maps:put("b", 1337, Map).
+#{"a" => 1,"b" => 1337}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="remove" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ The function removes the <c><anno>Key</anno></c>, if it exists, and its associated value from
+ <c><anno>Map1</anno></c> and returns a new map <c><anno>Map2</anno></c> without key <c><anno>Key</anno></c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"a" => 1}.
+#{"a" => 1}
+> maps:remove("a",Map).
+#{}
+> maps:remove("b",Map).
+#{"a" => 1}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="size" arity="1"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ The function returns the number of key-value associations in the <c><anno>Map</anno></c>.
+ This operation happens in constant time.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{42 => value_two,1337 => "value one","a" => 1},
+ maps:size(Map).
+3</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="to_list" arity="1"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ The fuction returns a list of pairs representing the key-value associations of <c><anno>Map</anno></c>,
+ where the pairs, <c>[{K1,V1}, ..., {Kn,Vn}]</c>, are returned in arbitrary order.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{42 => value_three,1337 => "value two","a" => 1},
+ maps:to_list(Map).
+[{42,value_three},{1337,"value two"},{"a",1}]</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="update" arity="3"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ If <c><anno>Key</anno></c> exists in <c><anno>Map1</anno></c> the old associated value is
+ replaced by value <c><anno>Value</anno></c>. The function returns a new map <c><anno>Map2</anno></c> containing
+ the new associated value. If <c><anno>Key</anno></c> does not exist in <c><anno>Map1</anno></c> an exception is
+ generated.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{"a" => 1}.
+#{"a" => 1}
+> maps:update("a", 42, Map).
+#{"a" => 42}</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="values" arity="1"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns a complete list of values, in arbitrary order, contained in map <c>M</c>.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{42 => value_three,1337 => "value two","a" => 1},
+ maps:values(Map).
+[value_three,"value two",1]</code>
+ </desc>
+ </func>
+
+ <func>
+ <name name="without" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns a new map <c><anno>Map2</anno></c> without the keys <c>K1</c> through <c>Kn</c> and their associated values from map <c><anno>Map1</anno></c>.
+ Any key in <c><anno>Ks</anno></c> that does not exist in <c><anno>Map1</anno></c> are ignored.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{42 => value_three,1337 => "value two","a" => 1},
+ Ks = ["a",42,"other key"],
+ maps:without(Ks,Map).
+#{1337 => "value two"}</code>
+ </desc>
+ </func>
+ </funcs>
+</erlref>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index e94a4d6a55..15e6fdfa9f 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -30,6 +30,390 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 2.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The option dupnames did not work as intended in re. When
+ looking for names with {capture, [Name, ...]}, re:run
+ returned a random instance of the match for that name,
+ instead of the leftmost matching instance, which was what
+ the documentation stated. This is now corrected to adhere
+ to the documentation. The option {capture,all_names}
+ along with a re:inspect/2 function is also added to
+ further help in using named subpatterns.</p>
+ <p>
+ Own Id: OTP-11205</p>
+ </item>
+ <item>
+ <p>
+ If option 'binary' was set for standard_input, then c:i()
+ would hang if the output was more than one page long -
+ i.e. then input after "(c)ontinue (q)uit --&gt;" could
+ not be read. This has been corrected. (Thanks to José
+ Valim)</p>
+ <p>
+ Own Id: OTP-11589</p>
+ </item>
+ <item>
+ <p>
+ stdlib/lists: Add function droplast/1 This functions
+ drops the last element of a non-empty list. lists:last/1
+ and lists:droplast/1 are the dual of hd/1 and tl/1 but
+ for the end of a list. (Thanks to Hans Svensson)</p>
+ <p>
+ Own Id: OTP-11677</p>
+ </item>
+ <item>
+ <p>
+ Allow all auto imports to be suppressed at once.
+ Introducing the no_auto_import attribute:
+ -compile(no_auto_import). Useful for code generation
+ tools that always use the qualified function names and
+ want to avoid the auto imported functions clashing with
+ local ones. (Thanks to José Valim.)</p>
+ <p>
+ Own Id: OTP-11682</p>
+ </item>
+ <item>
+ <p>
+ supervisor_bridge does no longer report normal
+ termination of children. The reason is that in some
+ cases, for instance when the restart strategy is
+ simple_one_for_one, the log could be completely
+ overloaded with reports about normally terminating
+ processes. (Thanks to Artem Ocheredko)</p>
+ <p>
+ Own Id: OTP-11685</p>
+ </item>
+ <item>
+ <p> The type annotations for alternative registries using
+ the {via, Module, Name} syntax for sup_name() and
+ sup_ref() in the supervisor module are now consistent
+ with the documentation. Dialyzer should no longer
+ complain about valid supervisor:start_link() and
+ supervisor:start_child() calls. (Thanks to Caleb
+ Helbling.) </p>
+ <p>
+ Own Id: OTP-11707</p>
+ </item>
+ <item>
+ <p> Two Dets bugs have been fixed. When trying to open a
+ short file that is not a Dets file, the file was deleted
+ even with just read access. Calling
+ <c>dets:is_dets_file/1</c> with a file that is not a Dets
+ file, a file descriptor was left open. (Thanks to HÃ¥kan
+ Mattsson for reporting the bugs.) </p>
+ <p>
+ Own Id: OTP-11709</p>
+ </item>
+ <item>
+ <p>
+ Fix race bug in <c>ets:all</c>. Concurrent creation of
+ tables could cause other tables to not be included in the
+ result. (Thanks to Florian Schintke for bug report)</p>
+ <p>
+ Own Id: OTP-11726</p>
+ </item>
+ <item>
+ <p>
+ erl_eval now properly evaluates '=='/2 when it is used in
+ guards. (Thanks to José Valim)</p>
+ <p>
+ Own Id: OTP-11747</p>
+ </item>
+ <item>
+ <p>
+ Calls to proplists:get_value/3 are replaced by the faster
+ lists:keyfind/3 in io_lib_pretty. Elements in the list
+ are always 2-tuples. (Thanks to Andrew Thompson)</p>
+ <p>
+ Own Id: OTP-11752</p>
+ </item>
+ <item>
+ <p> A qlc bug where filters were erroneously optimized
+ away has been fixed. Thanks to Sam Bobroff for reporting
+ the bug. </p>
+ <p>
+ Own Id: OTP-11758</p>
+ </item>
+ <item>
+ <p>
+ A number of compiler errors where unusual or nonsensical
+ code would crash the compiler have been reported by Ulf
+ Norell and corrected by Anthony Ramine.</p>
+ <p>
+ Own Id: OTP-11770</p>
+ </item>
+ <item>
+ <p> Since Erlang/OTP R16B the Erlang Core Linter
+ (<c>erl_lint</c>) has not emitted errors when built-in
+ types were re-defined. This bug has been fixed. (Thanks
+ to Roberto Aloi.) </p>
+ <p>
+ Own Id: OTP-11772</p>
+ </item>
+ <item>
+ <p>
+ The functions <c>sys:get_state/1,2</c> and
+ <c>sys:replace_state/2,3</c> are fixed so they can now be
+ run while the process is sys suspended. To accomplish
+ this, the new callbacks <c>Mod:system_get_state/1</c> and
+ <c>Mod:system_replace_state/2</c> are added, which are
+ also implemented by the generic behaviours
+ <c>gen_server</c>, <c>gen_event</c> and <c>gen_fsm</c>.</p>
+ <p>
+ The potential incompatibility refers to</p>
+ <p>
+ <list> <item>The previous behaviour of intercepting the
+ system message and passing a tuple of size 2 as the last
+ argument to <c>sys:handle_system_msg/6</c> is no longer
+ supported.</item> <item>The error handling when
+ <c>StateFun</c> in <c>sys:replace_state/2,3</c> fails is
+ changed from being totally silent to possibly (if the
+ callback module does not catch) throw an exception in the
+ client process.</item> </list></p>
+ <p>
+ (Thanks to James Fish and Steve Vinoski)</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11817</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Options to set match_limit and match_limit_recursion are
+ added to re:run. The option report_errors is also added
+ to get more information when re:run fails due to limits
+ or compilation errors.</p>
+ <p>
+ Own Id: OTP-10285</p>
+ </item>
+ <item>
+ <p> The pre-defined types <c>array/0</c>, <c>dict/0</c>,
+ <c>digraph/0</c>, <c>gb_set/0</c>, <c>gb_tree/0</c>,
+ <c>queue/0</c>, <c>set/0</c>, and <c>tid/0</c> have been
+ deprecated. They will be removed in Erlang/OTP 18.0. </p>
+ <p> Instead the types <c>array:array/0</c>,
+ <c>dict:dict/0</c>, <c>digraph:graph/0</c>,
+ <c>gb_set:set/0</c>, <c>gb_tree:tree/0</c>,
+ <c>queue:queue/0</c>, <c>sets:set/0</c>, and
+ <c>ets:tid/0</c> can be used. (Note: it has always been
+ necessary to use <c>ets:tid/0</c>.) </p> <p> It is
+ allowed in Erlang/OTP 17.0 to locally re-define the types
+ <c>array/0</c>, <c>dict/0</c>, and so on. </p> <p> New
+ types <c>array:array/1</c>, <c>dict:dict/2</c>,
+ <c>gb_sets:set/1</c>, <c>gb_trees:tree/2</c>,
+ <c>queue:queue/1</c>, and <c>sets:set/1</c> have been
+ added. </p> <p> A compiler option,
+ <c>nowarn_deprecated_type</c>, has been introduced. By
+ including the attribute </p> <c>
+ -compile(nowarn_deprecated_type).</c> <p> in an Erlang
+ source file, warnings about deprecated types can be
+ avoided in Erlang/OTP 17.0. </p> <p> The option can also
+ be given as a compiler flag: </p> <c> erlc
+ +nowarn_deprecated_type file.erl</c>
+ <p>
+ Own Id: OTP-10342</p>
+ </item>
+ <item>
+ <p>
+ Calls to erlang:open_port/2 with 'spawn' are updated to
+ handle space in the command path.</p>
+ <p>
+ Own Id: OTP-10842</p>
+ </item>
+ <item>
+ <p> Dialyzer's <c>unmatched_return</c> warnings have been
+ corrected. </p>
+ <p>
+ Own Id: OTP-10908</p>
+ </item>
+ <item>
+ <p>
+ Forbid unsized fields in patterns of binary generators
+ and simplified v3_core's translation of bit string
+ generators. (Thanks to Anthony Ramine.)</p>
+ <p>
+ Own Id: OTP-11186</p>
+ </item>
+ <item>
+ <p>
+ The version of the PCRE library Used by Erlang's re
+ module is raised to 8.33 from 7.6. This means, among
+ other things, better Unicode and Unicode Character
+ Properties support. New options connected to PCRE 8.33
+ are also added to the re module (ucd, notempty_atstart,
+ no_start_optimize). PCRE has extended the regular
+ expression syntax between 7.6 and 8.33, why this imposes
+ a potential incompatibility. Only very complicated
+ regular expressions may be affected, but if you know you
+ are using obscure features, please test run your regular
+ expressions and verify that their behavior has not
+ changed.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11204</p>
+ </item>
+ <item>
+ <p>
+ Added dict:is_empty/1 and orddict:is_empty/1. (Thanks to
+ Magnus Henoch.)</p>
+ <p>
+ Own Id: OTP-11353</p>
+ </item>
+ <item>
+ <p>
+ A call to either the <c>garbage_collect/1</c> BIF or the
+ <c>check_process_code/2</c> BIF may trigger garbage
+ collection of another processes than the process calling
+ the BIF. The previous implementations performed these
+ kinds of garbage collections without considering the
+ internal state of the process being garbage collected. In
+ order to be able to more easily and more efficiently
+ implement yielding native code, these types of garbage
+ collections have been rewritten. A garbage collection
+ like this is now triggered by an asynchronous request
+ signal, the actual garbage collection is performed by the
+ process being garbage collected itself, and finalized by
+ a reply signal to the process issuing the request. Using
+ this approach processes can disable garbage collection
+ and yield without having to set up the heap in a state
+ that can be garbage collected.</p>
+ <p>
+ The <seealso
+ marker="erts:erlang#garbage_collect/2"><c>garbage_collect/2</c></seealso>,
+ and <seealso
+ marker="erts:erlang#check_process_code/3"><c>check_process_code/3</c></seealso>
+ BIFs have been introduced. Both taking an option list as
+ last argument. Using these, one can issue asynchronous
+ requests.</p>
+ <p>
+ <c>code:purge/1</c> and <c>code:soft_purge/1</c> have
+ been rewritten to utilize asynchronous
+ <c>check_process_code</c> requests in order to
+ parallelize work.</p>
+ <p>
+ Characteristics impact: A call to the
+ <c>garbage_collect/1</c> BIF or the
+ <c>check_process_code/2</c> BIF will normally take longer
+ time to complete while the system as a whole wont be as
+ much negatively effected by the operation as before. A
+ call to <c>code:purge/1</c> and <c>code:soft_purge/1</c>
+ may complete faster or slower depending on the state of
+ the system while the system as a whole wont be as much
+ negatively effected by the operation as before.</p>
+ <p>
+ Own Id: OTP-11388 Aux Id: OTP-11535, OTP-11648 </p>
+ </item>
+ <item>
+ <p> Improve the documentation of the supervisor's
+ <c>via</c> reference. (Thanks to MaximMinin.) </p>
+ <p>
+ Own Id: OTP-11399</p>
+ </item>
+ <item>
+ <p><c>orddict:from_list/1</c> now uses the optimized sort
+ routines in the <c>lists</c> module instead of
+ (essentially) an insertion sort. Depending on the input
+ data, the speed of the new <c>from_list/1</c> is anything
+ from slightly faster up to several orders of magnitude
+ faster than the old <c>from_list/1</c>.</p> (Thanks to
+ Steve Vinoski.)
+ <p>
+ Own Id: OTP-11552</p>
+ </item>
+ <item>
+ <p>
+ EEP43: New data type - Maps</p>
+ <p>
+ With Maps you may for instance: <taglist> <item><c>M0 =
+ #{ a =&gt; 1, b =&gt; 2}, % create
+ associations</c></item> <item><c>M1 = M0#{ a := 10 }, %
+ update values</c></item> <item><c>M2 = M1#{ "hi" =&gt;
+ "hello"}, % add new associations</c></item> <item><c>#{
+ "hi" := V1, a := V2, b := V3} = M2. % match keys with
+ values</c></item> </taglist></p>
+ <p>
+ For information on how to use Maps please see the
+ <seealso marker="doc/reference_manual:maps">Reference
+ Manual</seealso>.</p>
+ <p>
+ The current implementation is without the following
+ features: <taglist> <item>No variable keys</item>
+ <item>No single value access</item> <item>No map
+ comprehensions</item> </taglist></p>
+ <p>
+ Note that Maps is <em>experimental</em> during OTP 17.0.</p>
+ <p>
+ Own Id: OTP-11616</p>
+ </item>
+ <item>
+ <p>
+ When tab completing the erlang shell now expands
+ zero-arity functions all the way to closing parenthesis,
+ unless there is another function with the same name and a
+ different arity. (Thanks to Pierre Fenoll.)</p>
+ <p>
+ Own Id: OTP-11684</p>
+ </item>
+ <item>
+ <p> The Erlang Code Preprocessor (<c>epp</c>) could loop
+ when encountering a circular macro definition in an
+ included file. This bug has been fixed. </p> <p> Thanks
+ to Maruthavanan Subbarayan for reporting the bug, and to
+ Richard Carlsson for providing a bug fix. </p>
+ <p>
+ Own Id: OTP-11728</p>
+ </item>
+ <item>
+ <p> The Erlang Code Linter (<c>erl_lint</c>) has since
+ Erlang/OTP R13B emitted warnings whenever any of the
+ types <c>arity()</c>, <c>bitstring()</c>,
+ <c>iodata()</c>, or <c>boolean()</c> were re-defined. Now
+ errors are emitted instead. </p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11771</p>
+ </item>
+ <item>
+ <p> The <c>encoding</c> option of
+ <c>erl_parse:abstract/2</c> has been extended to include
+ <c>none</c> and a callback function (a predicate). </p>
+ <p>
+ Own Id: OTP-11807</p>
+ </item>
+ <item>
+ <p>
+ Export zip option types to allow referal from other
+ modules.</p>
+ <p>
+ Thanks to Pierre Fenoll and HÃ¥kan Mattson</p>
+ <p>
+ Own Id: OTP-11828</p>
+ </item>
+ <item>
+ <p>
+ The module <c>pg</c> has been deprecated and will be
+ removed in Erlang/OTP 18.</p>
+ <p>
+ Own Id: OTP-11840</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 1.19.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/pg.xml b/lib/stdlib/doc/src/pg.xml
index 7cc1b805b4..a3b69884b6 100644
--- a/lib/stdlib/doc/src/pg.xml
+++ b/lib/stdlib/doc/src/pg.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,9 @@
<module>pg</module>
<modulesummary>Distributed, Named Process Groups</modulesummary>
<description>
+ <warning>
+ <p>This module is deprecated and will be removed in Erlang/OTP 18.</p>
+ </warning>
<p>This (experimental) module implements process groups. A process
group is a group of processes that can be accessed by a common
name. For example, a group named <c>foobar</c> can include a set
diff --git a/lib/stdlib/doc/src/ref_man.xml b/lib/stdlib/doc/src/ref_man.xml
index 4ecd02a4bf..6c35578bdf 100644
--- a/lib/stdlib/doc/src/ref_man.xml
+++ b/lib/stdlib/doc/src/ref_man.xml
@@ -68,6 +68,7 @@
<xi:include href="lib.xml"/>
<xi:include href="lists.xml"/>
<xi:include href="log_mf_h.xml"/>
+ <xi:include href="maps.xml"/>
<xi:include href="math.xml"/>
<xi:include href="ms_transform.xml"/>
<xi:include href="orddict.xml"/>
diff --git a/lib/stdlib/doc/src/specs.xml b/lib/stdlib/doc/src/specs.xml
index 213ce7563f..60a04ed5e7 100644
--- a/lib/stdlib/doc/src/specs.xml
+++ b/lib/stdlib/doc/src/specs.xml
@@ -34,6 +34,7 @@
<xi:include href="../specs/specs_lib.xml"/>
<xi:include href="../specs/specs_lists.xml"/>
<xi:include href="../specs/specs_log_mf_h.xml"/>
+ <xi:include href="../specs/specs_maps.xml"/>
<xi:include href="../specs/specs_math.xml"/>
<xi:include href="../specs/specs_ms_transform.xml"/>
<xi:include href="../specs/specs_orddict.xml"/>
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index 8197684d2d..3a5027d595 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -262,12 +262,12 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
locally as <c>Name</c> using <c>register/2</c>. If
<c><anno>SupName</anno>={global,Name}</c> the supervisor is registered
globally as <c>Name</c> using <c>global:register_name/2</c>. If
- <c><anno>SupName</anno>={via,Module,Name}</c> the supervisor
+ <c><anno>SupName</anno>={via,<anno>Module</anno>,<anno>Name</anno>}</c> the supervisor
is registered as <c>Name</c> using the registry represented by
<c>Module</c>. The <c>Module</c> callback should export the functions
<c>register_name/2</c>, <c>unregister_name/1</c> and <c>send/2</c>,
which should behave like the corresponding functions in <c>global</c>.
- Thus, <c>{via,global,Name}</c> is a valid reference.</p>
+ Thus, <c>{via,global,<anno>Name</anno>}</c> is a valid reference.</p>
<p>If no name is provided, the supervisor is not registered.</p>
<p><c><anno>Module</anno></c> is the name of the callback module.</p>
<p><c><anno>Args</anno></c> is an arbitrary term which is passed as
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index ab8b380f49..a46fa1289f 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -246,6 +246,22 @@
<c>{Module, Id, HandlerState}</c>, where <c>Module</c> is the event handler's module name,
<c>Id</c> is the handler's ID (which is the value <c>false</c> if it was registered without
an ID), and <c>HandlerState</c> is the handler's state.</p>
+ <p>If the callback module exports a <c>system_get_state/1</c> function, it will be called in the
+ target process to get its state. Its argument is the same as the <c>Misc</c> value returned by
+ <seealso marker="#get_status-1">get_status/1,2</seealso>, and the <c>system_get_state/1</c>
+ function is expected to extract the callback module's state from it. The <c>system_get_state/1</c>
+ function must return <c>{ok, State}</c> where <c>State</c> is the callback module's state.</p>
+ <p>If the callback module does not export a <c>system_get_state/1</c> function, <c>get_state/1,2</c>
+ assumes the <c>Misc</c> value is the callback module's state and returns it directly instead.</p>
+ <p>If the callback module's <c>system_get_state/1</c> function crashes or throws an exception, the
+ caller exits with error <c>{callback_failed, {Module, system_get_state}, {Class, Reason}}</c> where
+ <c>Module</c> is the name of the callback module and <c>Class</c> and <c>Reason</c> indicate
+ details of the exception.</p>
+ <p>The <c>system_get_state/1</c> function is primarily useful for user-defined
+ behaviours and modules that implement OTP <seealso marker="#special_process">special
+ processes</seealso>. The <c>gen_server</c>, <c>gen_fsm</c>, and <c>gen_event</c> OTP
+ behaviour modules export this function, and so callback modules for those behaviours
+ need not supply their own.</p>
<p>To obtain more information about a process, including its state, see
<seealso marker="#get_status-1">get_status/1</seealso> and
<seealso marker="#get_status-2">get_status/2</seealso>.</p>
@@ -289,6 +305,28 @@
function means that only the state of the particular event handler it was working on when it
failed or crashed is unchanged; it can still succeed in changing the states of other event
handlers registered in the same <c>gen_event</c> process.</p>
+ <p>If the callback module exports a <c>system_replace_state/2</c> function, it will be called in the
+ target process to replace its state using <c>StateFun</c>. Its two arguments are <c>StateFun</c>
+ and <c>Misc</c>, where <c>Misc</c> is the same as the <c>Misc</c> value returned by
+ <seealso marker="#get_status-1">get_status/1,2</seealso>. A <c>system_replace_state/2</c> function
+ is expected to return <c>{ok, NewState, NewMisc}</c> where <c>NewState</c> is the callback module's
+ new state obtained by calling <c>StateFun</c>, and <c>NewMisc</c> is a possibly new value used to
+ replace the original <c>Misc</c> (required since <c>Misc</c> often contains the callback
+ module's state within it).</p>
+ <p>If the callback module does not export a <c>system_replace_state/2</c> function,
+ <c>replace_state/2,3</c> assumes the <c>Misc</c> value is the callback module's state, passes it
+ to <c>StateFun</c> and uses the return value as both the new state and as the new value of
+ <c>Misc</c>.</p>
+ <p>If the callback module's <c>system_replace_state/2</c> function crashes or throws an exception,
+ the caller exits with error <c>{callback_failed, {Module, system_replace_state}, {Class, Reason}}</c>
+ where <c>Module</c> is the name of the callback module and <c>Class</c> and <c>Reason</c> indicate details
+ of the exception. If the callback module does not provide a <c>system_replace_state/2</c> function and
+ <c>StateFun</c> crashes or throws an exception, the caller exits with error
+ <c>{callback_failed, StateFun, {Class, Reason}}</c>.</p>
+ <p>The <c>system_replace_state/2</c> function is primarily useful for user-defined behaviours and
+ modules that implement OTP <seealso marker="#special_process">special processes</seealso>. The
+ <c>gen_server</c>, <c>gen_fsm</c>, and <c>gen_event</c> OTP behaviour modules export this function,
+ and so callback modules for those behaviours need not supply their own.</p>
</desc>
</func>
<func>
@@ -322,7 +360,7 @@
<section>
<title>Process Implementation Functions</title>
- <p>The following functions are used when implementing a
+ <p><marker id="special_process"/>The following functions are used when implementing a
special process. This is an ordinary process which does not use a
standard behaviour, but a process which understands the standard system messages.</p>
</section>
@@ -375,8 +413,9 @@
process continues the execution, or
<c><anno>Module</anno>:system_terminate(Reason, <anno>Parent</anno>, <anno>Debug</anno>, <anno>Misc</anno>)</c> if
the process should terminate. The <c><anno>Module</anno></c> must export
- <c>system_continue/3</c>, <c>system_terminate/4</c>, and
- <c>system_code_change/4</c> (see below).
+ <c>system_continue/3</c>, <c>system_terminate/4</c>,
+ <c>system_code_change/4</c>, <c>system_get_state/1</c> and
+ <c>system_replace_state/2</c> (see below).
</p>
<p>The <c><anno>Misc</anno></c> argument can be used to save internal data
in a process, for example its state. It is sent to
@@ -444,6 +483,34 @@
defined, the atom <c>undefined</c> is sent.</p>
</desc>
</func>
+ <func>
+ <name>Mod:system_get_state(Misc) -> {ok, State}</name>
+ <fsummary>Called when the process should return its current state</fsummary>
+ <type>
+ <v>Misc = term()</v>
+ <v>State = term()</v>
+ </type>
+ <desc>
+ <p>This function is called from <c>sys:handle_system_msg/6</c> when the process
+ should return a term that reflects its current state. <c>State</c> is the
+ value returned by <c>sys:get_state/2</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>Mod:system_replace_state(StateFun, Misc) -> {ok, NState, NMisc}</name>
+ <fsummary>Called when the process should replace its current state</fsummary>
+ <type>
+ <v>StateFun = fun((State :: term()) -> NState)</v>
+ <v>Misc = term()</v>
+ <v>NState = term()</v>
+ <v>NMisc = term()</v>
+ </type>
+ <desc>
+ <p>This function is called from <c>sys:handle_system_msg/6</c> when the process
+ should replace its current state. <c>NState</c> is the value returned by
+ <c>sys:replace_state/3</c>.</p>
+ </desc>
+ </func>
</funcs>
</erlref>
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index 75505d7d84..bebfbd4514 100644
--- a/lib/stdlib/doc/src/unicode_usage.xml
+++ b/lib/stdlib/doc/src/unicode_usage.xml
@@ -848,6 +848,7 @@ Eshell V5.10.1 (abort with ^G)
</section>
<section>
<title>Unicode in Environment and Parameters</title>
+ <marker id="unicode_in_environment_and_parameters"/>
<p>Environment variables and their interpretation is handled much in
the same way as file names. If Unicode file names are enabled,
environment variables as well as parameters to the Erlang VM are
@@ -996,7 +997,8 @@ ok
</pre>
</section>
<section>
- <title><marker id="unicode_options_summary"/>Summary of Options</title>
+ <title>Summary of Options</title>
+ <marker id="unicode_options_summary"/>
<p>The Unicode support is controlled by both command line switches,
some standard environment variables and the version of OTP you are
using. Most options affect mainly the way Unicode data is displayed,
diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml
index 66c21da193..48b376743d 100644
--- a/lib/stdlib/doc/src/zip.xml
+++ b/lib/stdlib/doc/src/zip.xml
@@ -123,6 +123,18 @@
</taglist>
</desc>
</datatype>
+ <datatype>
+ <name name="filename"/>
+ <p>The name of a zip file.</p>
+ </datatype>
+ <datatype><name name="extension"/></datatype>
+ <datatype><name name="extension_spec"/></datatype>
+ <datatype>
+ <name name="create_option"/>
+ <desc>
+ <p>These options are described in <seealso marker="#zip_options">create/3</seealso>.</p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
<func>
@@ -160,6 +172,7 @@
set to <c>["gif", "jpg"]</c> and <c>uncompress</c> is set to
<c>["jpg"]</c>, only files with <c>"gif"</c> as extension will
be compressed. No other files will be compressed.</p>
+ <marker id="zip_options"></marker>
<p>The following options are available:</p>
<taglist>
<tag><c>cooked</c></tag>
diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl
index 5fcb74310e..e71e26e51a 100644
--- a/lib/stdlib/examples/erl_id_trans.erl
+++ b/lib/stdlib/examples/erl_id_trans.erl
@@ -148,7 +148,7 @@ pattern({map,Line,Ps0}) ->
Ps1 = pattern_list(Ps0),
{map,Line,Ps1};
pattern({map_field_exact,Line,K,V}) ->
- Ke = pattern(K),
+ Ke = expr(K),
Ve = pattern(V),
{map_field_exact,Line,Ke,Ve};
%%pattern({struct,Line,Tag,Ps0}) ->
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index 44dad04f43..c32da1624f 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1785,6 +1785,7 @@ read_file_header(FileName, Access, RamFile) ->
Version =:= 9 ->
dets_v9:read_file_header(Fd, FileName);
true ->
+ _ = file:close(Fd),
throw({error, {not_a_dets_file, FileName}})
end.
@@ -2113,6 +2114,8 @@ test_bchunk_format(Head, Term) ->
do_open_file([Fname, Verbose], Parent, Server, Ref) ->
case catch fopen2(Fname, Ref) of
+ {error, {tooshort, _}} ->
+ err({error, {not_a_dets_file, Fname}});
{error, _Reason} = Error ->
err(Error);
{ok, Head} ->
@@ -2126,11 +2129,10 @@ do_open_file([Fname, Verbose], Parent, Server, Ref) ->
[Bad]),
{error, {dets_bug, Fname, Bad}}
end;
-do_open_file([Tab, OpenArgs, Verb], Parent, Server, Ref) ->
+do_open_file([Tab, OpenArgs, Verb], Parent, Server, _Ref) ->
case catch fopen3(Tab, OpenArgs) of
{error, {tooshort, _}} ->
- _ = file:delete(OpenArgs#open_args.file),
- do_open_file([Tab, OpenArgs, Verb], Parent, Server, Ref);
+ err({error, {not_a_dets_file, OpenArgs#open_args.file}});
{error, _Reason} = Error ->
err(Error);
{ok, Head} ->
@@ -2486,7 +2488,6 @@ fopen2(Fname, Tab) ->
{ok, _} ->
Acc = read_write,
Ram = false,
- %% Fd is not always closed upon error, but exit is soon called.
{ok, Fd, FH} = read_file_header(Fname, Acc, Ram),
Mod = FH#fileheader.mod,
Do = case Mod:check_file_header(FH, Fd) of
@@ -2542,7 +2543,6 @@ fopen_existing_file(Tab, OpenArgs) ->
ram_file = Ram, delayed_write = CacheSz, auto_save =
Auto, access = Acc, version = Version, debug = Debug} =
OpenArgs,
- %% Fd is not always closed upon error, but exit is soon called.
{ok, Fd, FH} = read_file_header(Fname, Acc, Ram),
V9 = (Version =:= 9) or (Version =:= default),
MinF = (MinSlots =:= default) or (MinSlots =:= FH#fileheader.min_no_slots),
diff --git a/lib/stdlib/src/dict.erl b/lib/stdlib/src/dict.erl
index 6088e1a2dd..cf8fb3114a 100644
--- a/lib/stdlib/src/dict.erl
+++ b/lib/stdlib/src/dict.erl
@@ -55,8 +55,7 @@
-define(exp_size, (?seg_size * ?expand_load)).
-define(con_size, (?seg_size * ?contract_load)).
--type segs(K, V) :: tuple()
- | {K, V}. % dummy
+-type segs(_Key, _Value) :: tuple().
%% Define a hashtable. The default values are the standard ones.
-record(dict,
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 68e079b7e5..9b506b0a44 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -20,12 +20,12 @@
%% An Erlang code preprocessor.
--export([open/2,open/3,open/5,close/1,format_error/1]).
+-export([open/1, open/2,open/3,open/5,close/1,format_error/1]).
-export([scan_erl_form/1,parse_erl_form/1,macro_defs/1]).
--export([parse_file/1, parse_file/3]).
+-export([parse_file/1, parse_file/2, parse_file/3]).
-export([default_encoding/0, encoding_to_string/1,
read_encoding_from_binary/1, read_encoding_from_binary/2,
- set_encoding/1, read_encoding/1, read_encoding/2]).
+ set_encoding/1, set_encoding/2, read_encoding/1, read_encoding/2]).
-export([interpret_file_attribute/1]).
-export([normalize_typed_record_fields/1,restore_typed_record_fields/1]).
@@ -33,21 +33,34 @@
-export_type([source_encoding/0]).
--type macros() :: [{atom(), term()}].
+-type macros() :: [atom() | {atom(), term()}].
-type epp_handle() :: pid().
-type source_encoding() :: latin1 | utf8.
+-type ifdef() :: 'ifdef' | 'ifndef' | 'else'.
+
+-type name() :: {'atom', atom()}.
+-type argspec() :: 'none' %No arguments
+ | non_neg_integer(). %Number of arguments
+-type tokens() :: [erl_scan:token()].
+-type used() :: {name(), argspec()}.
+
+-define(DEFAULT_ENCODING, utf8).
+
%% Epp state record.
--record(epp, {file, %Current file
- location, %Current location
- delta, %Offset from Location (-file)
- name="", %Current file name
- name2="", %-"-, modified by -file
- istk=[], %Ifdef stack
- sstk=[], %State stack
- path=[], %Include-path
- macs = dict:new() :: dict:dict(),%Macros (don't care locations)
- uses = dict:new() :: dict:dict(),%Macro use structure
+-record(epp, {file :: file:io_device(), %Current file
+ location=1, %Current location
+ delta=0 :: non_neg_integer(), %Offset from Location (-file)
+ name="" :: file:name(), %Current file name
+ name2="" :: file:name(), %-"-, modified by -file
+ istk=[] :: [ifdef()], %Ifdef stack
+ sstk=[] :: [#epp{}], %State stack
+ path=[] :: [file:name()], %Include-path
+ macs = dict:new() %Macros (don't care locations)
+ :: dict:dict(name(), {argspec(), tokens()}),
+ uses = dict:new() %Macro use structure
+ :: dict:dict(name(), [{argspec(), [used()]}]),
+ default_encoding = ?DEFAULT_ENCODING :: source_encoding(),
pre_opened = false :: boolean()
}).
@@ -58,6 +71,7 @@
%%% distinction in the internal representation would simplify the code
%%% a little.
+%% open(Options)
%% open(FileName, IncludePath)
%% open(FileName, IncludePath, PreDefMacros)
%% open(FileName, IoDevice, StartLocation, IncludePath, PreDefMacros)
@@ -65,6 +79,7 @@
%% scan_erl_form(Epp)
%% parse_erl_form(Epp)
%% parse_file(Epp)
+%% parse_file(FileName, Options)
%% parse_file(FileName, IncludePath, PreDefMacros)
%% macro_defs(Epp)
@@ -87,14 +102,43 @@ open(Name, Path) ->
ErrorDescriptor :: term().
open(Name, Path, Pdm) ->
- Self = self(),
- Epp = spawn(fun() -> server(Self, Name, Path, Pdm) end),
- epp_request(Epp).
+ internal_open([{name, Name}, {includes, Path}, {macros, Pdm}], #epp{}).
open(Name, File, StartLocation, Path, Pdm) ->
- Self = self(),
- Epp = spawn(fun() -> server(Self, Name, File, StartLocation,Path,Pdm) end),
- epp_request(Epp).
+ internal_open([{name, Name}, {includes, Path}, {macros, Pdm}],
+ #epp{file=File, pre_opened=true, location=StartLocation}).
+
+-spec open(Options) ->
+ {'ok', Epp} | {'ok', Epp, Extra} | {'error', ErrorDescriptor} when
+ Options :: [{'default_encoding', DefEncoding :: source_encoding()} |
+ {'includes', IncludePath :: [DirectoryName :: file:name()]} |
+ {'macros', PredefMacros :: macros()} |
+ {'name',FileName :: file:name()} |
+ 'extra'],
+ Epp :: epp_handle(),
+ Extra :: [{'encoding', source_encoding() | 'none'}],
+ ErrorDescriptor :: term().
+
+open(Options) ->
+ internal_open(Options, #epp{}).
+
+internal_open(Options, St) ->
+ case proplists:get_value(name, Options) of
+ undefined ->
+ erlang:error(badarg);
+ Name ->
+ Self = self(),
+ Epp = spawn(fun() -> server(Self, Name, Options, St) end),
+ case epp_request(Epp) of
+ {ok, Pid, Encoding} ->
+ case proplists:get_bool(extra, Options) of
+ true -> {ok, Pid, [{encoding, Encoding}]};
+ false -> {ok, Pid}
+ end;
+ Other ->
+ Other
+ end
+ end.
-spec close(Epp) -> 'ok' when
Epp :: epp_handle().
@@ -170,9 +214,6 @@ format_error({'NYI',What}) ->
io_lib:format("not yet implemented '~s'", [What]);
format_error(E) -> file:format_error(E).
-%% parse_file(FileName, IncludePath, [PreDefMacro]) ->
-%% {ok,[Form]} | {error,OpenError}
-
-spec parse_file(FileName, IncludePath, PredefMacros) ->
{'ok', [Form]} | {error, OpenError} when
FileName :: file:name(),
@@ -184,17 +225,40 @@ format_error(E) -> file:format_error(E).
OpenError :: file:posix() | badarg | system_limit.
parse_file(Ifile, Path, Predefs) ->
- case open(Ifile, Path, Predefs) of
+ parse_file(Ifile, [{includes, Path}, {macros, Predefs}]).
+
+-spec parse_file(FileName, Options) ->
+ {'ok', [Form]} | {'ok', [Form], Extra} | {error, OpenError} when
+ FileName :: file:name(),
+ Options :: [{'includes', IncludePath :: [DirectoryName :: file:name()]} |
+ {'macros', PredefMacros :: macros()} |
+ {'default_encoding', DefEncoding :: source_encoding()} |
+ 'extra'],
+ Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line},
+ Line :: erl_scan:line(),
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(),
+ Extra :: [{'encoding', source_encoding() | 'none'}],
+ OpenError :: file:posix() | badarg | system_limit.
+
+parse_file(Ifile, Options) ->
+ case internal_open([{name, Ifile} | Options], #epp{}) of
{ok,Epp} ->
Forms = parse_file(Epp),
close(Epp),
{ok,Forms};
+ {ok,Epp,Extra} ->
+ Forms = parse_file(Epp),
+ close(Epp),
+ {ok,Forms,Extra};
{error,E} ->
{error,E}
end.
-%% parse_file(Epp) ->
-%% [Form]
+-spec parse_file(Epp) -> [Form] when
+ Epp :: epp_handle(),
+ Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line},
+ Line :: erl_scan:line(),
+ ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().
parse_file(Epp) ->
case parse_erl_form(Epp) of
@@ -219,8 +283,6 @@ parse_file(Epp) ->
[{eof,Location}]
end.
--define(DEFAULT_ENCODING, utf8).
-
-spec default_encoding() -> source_encoding().
default_encoding() ->
@@ -258,9 +320,16 @@ read_encoding(Name, Options) ->
File :: io:device(). % pid(); raw files don't work
set_encoding(File) ->
+ set_encoding(File, ?DEFAULT_ENCODING).
+
+-spec set_encoding(File, Default) -> source_encoding() | none when
+ Default :: source_encoding(),
+ File :: io:device(). % pid(); raw files don't work
+
+set_encoding(File, Default) ->
Encoding = read_encoding_from_file(File, true),
Enc = case Encoding of
- none -> default_encoding();
+ none -> Default;
Encoding -> Encoding
end,
ok = io:setopts(File, [{encoding, Enc}]),
@@ -446,35 +515,37 @@ restore_typed_record_fields([{attribute,La,type,{{record,Record},Fields,[]}}|
restore_typed_record_fields([Form|Forms]) ->
[Form|restore_typed_record_fields(Forms)].
-%% server(StarterPid, FileName, Path, PreDefMacros)
-
-server(Pid, Name, Path, Pdm) ->
+server(Pid, Name, Options, #epp{pre_opened=PreOpened}=St) ->
process_flag(trap_exit, true),
- case file:open(Name, [read]) of
- {ok,File} ->
- Location = 1,
- init_server(Pid, Name, File, Location, Path, Pdm, false);
- {error,E} ->
- epp_reply(Pid, {error,E})
+ case PreOpened of
+ false ->
+ case file:open(Name, [read]) of
+ {ok,File} ->
+ init_server(Pid, Name, Options, St#epp{file = File});
+ {error,E} ->
+ epp_reply(Pid, {error,E})
+ end;
+ true ->
+ init_server(Pid, Name, Options, St)
end.
-%% server(StarterPid, FileName, IoDevice, Location, Path, PreDefMacros)
-server(Pid, Name, File, AtLocation, Path, Pdm) ->
- process_flag(trap_exit, true),
- init_server(Pid, Name, File, AtLocation, Path, Pdm, true).
-
-init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) ->
+init_server(Pid, Name, Options, St0) ->
+ Pdm = proplists:get_value(macros, Options, []),
Ms0 = predef_macros(Name),
case user_predef(Pdm, Ms0) of
{ok,Ms1} ->
- _ = set_encoding(File),
- epp_reply(Pid, {ok,self()}),
+ #epp{file = File, location = AtLocation} = St0,
+ DefEncoding = proplists:get_value(default_encoding, Options,
+ ?DEFAULT_ENCODING),
+ Encoding = set_encoding(File, DefEncoding),
+ epp_reply(Pid, {ok,self(),Encoding}),
%% ensure directory of current source file is
%% first in path
- Path1 = [filename:dirname(Name) | Path],
- St = #epp{file=File, location=AtLocation, delta=0,
- name=Name, name2=Name, path=Path1, macs=Ms1,
- pre_opened = Pre},
+ Path = [filename:dirname(Name) |
+ proplists:get_value(includes, Options, [])],
+ St = St0#epp{delta=0, name=Name, name2=Name,
+ path=Path, macs=Ms1,
+ default_encoding=DefEncoding},
From = wait_request(St),
enter_file_reply(From, Name, AtLocation, AtLocation),
wait_req_scan(St);
@@ -600,9 +671,11 @@ enter_file2(NewF, Pname, From, St0, AtLocation) ->
%% the path) must be dropped, otherwise the path used within the current
%% file will depend on the order of file inclusions in the parent files
Path = [filename:dirname(Pname) | tl(St0#epp.path)],
- _ = set_encoding(NewF),
+ DefEncoding = St0#epp.default_encoding,
+ _ = set_encoding(NewF, DefEncoding),
#epp{file=NewF,location=Loc,name=Pname,name2=Pname,delta=0,
- sstk=[St0|St0#epp.sstk],path=Path,macs=Ms}.
+ sstk=[St0|St0#epp.sstk],path=Path,macs=Ms,
+ default_encoding=DefEncoding}.
enter_file_reply(From, Name, Location, AtLocation) ->
Attr = loc_attr(AtLocation),
diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl
index ed8fea5d78..caed4d41d6 100644
--- a/lib/stdlib/src/erl_compile.erl
+++ b/lib/stdlib/src/erl_compile.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -66,7 +66,7 @@ my_halt(Reason) ->
compile(List) ->
process_flag(trap_exit, true),
- Pid = spawn_link(fun() -> compiler_runner(List) end),
+ Pid = spawn_link(compiler_runner(List)),
receive
{'EXIT', Pid, {compiler_result, Result}} ->
Result;
@@ -79,14 +79,16 @@ compile(List) ->
error
end.
--spec compiler_runner([cmd_line_arg()]) -> no_return().
+-spec compiler_runner([cmd_line_arg()]) -> fun(() -> no_return()).
compiler_runner(List) ->
- %% We don't want the current directory in the code path.
- %% Remove it.
- Path = [D || D <- code:get_path(), D =/= "."],
- true = code:set_path(Path),
- exit({compiler_result, compile1(List)}).
+ fun() ->
+ %% We don't want the current directory in the code path.
+ %% Remove it.
+ Path = [D || D <- code:get_path(), D =/= "."],
+ true = code:set_path(Path),
+ exit({compiler_result, compile1(List)})
+ end.
%% Parses the first part of the option list.
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 3a4108e297..3cfedfee97 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -18,6 +18,9 @@
%%
-module(erl_eval).
+%% Guard is_map/1 is not yet supported in HiPE.
+-compile(no_native).
+
%% An evaluator for Erlang abstract syntax.
-export([exprs/2,exprs/3,exprs/4,expr/2,expr/3,expr/4,expr/5,
@@ -241,13 +244,20 @@ expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
erlang:raise(error, {undef_record,Name}, stacktrace());
%% map
-expr({map,_, Binding,Es}, Bs0, Lf, Ef, RBs) ->
- {value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, RBs),
- {Vs,Bs} = eval_map_fields(Es, Bs1, Lf, Ef),
- ret_expr(lists:foldl(fun
- ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi);
- ({map_exact,K,V}, Mi) -> maps:update(K,V,Mi)
- end, Map0, Vs), Bs, RBs);
+expr({map,_,Binding,Es}, Bs0, Lf, Ef, RBs) ->
+ {value, Map0, Bs1} = expr(Binding, Bs0, Lf, Ef, none),
+ case Map0 of
+ #{} ->
+ {Vs,Bs2} = eval_map_fields(Es, Bs0, Lf, Ef),
+ Map1 = lists:foldl(fun ({map_assoc,K,V}, Mi) ->
+ maps:put(K, V, Mi);
+ ({map_exact,K,V}, Mi) ->
+ maps:update(K, V, Mi)
+ end, Map0, Vs),
+ ret_expr(Map1, merge_bindings(Bs2, Bs1), RBs);
+ _ ->
+ erlang:raise(error, {badarg,Map0}, stacktrace())
+ end;
expr({map,_,Es}, Bs0, Lf, Ef, RBs) ->
{Vs,Bs} = eval_map_fields(Es, Bs0, Lf, Ef),
ret_expr(lists:foldl(fun
@@ -1113,9 +1123,10 @@ match1({tuple,_,Elts}, Tuple, Bs, BBs)
match_tuple(Elts, Tuple, 1, Bs, BBs);
match1({tuple,_,_}, _, _Bs, _BBs) ->
throw(nomatch);
-match1({map,_,Fs}, Map, Bs, BBs) ->
+match1({map,_,Fs}, #{}=Map, Bs, BBs) ->
match_map(Fs, Map, Bs, BBs);
-
+match1({map,_,_}, _, _Bs, _BBs) ->
+ throw(nomatch);
match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs) ->
eval_bits:match_bits(Fs, B, Bs0, BBs,
match_fun(BBs),
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index f53c6e1278..57e768ba9d 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -135,10 +135,10 @@ pattern({tuple,Line,Ps}, St0) ->
pattern({map,Line,Ps}, St0) ->
{TPs,St1} = pattern_list(Ps, St0),
{{map,Line,TPs},St1};
-pattern({map_field_exact,Line,Key0,V0}, St0) ->
- {Key,St1} = pattern(Key0, St0),
+pattern({map_field_exact,Line,K0,V0}, St0) ->
+ {K,St1} = expr(K0, St0),
{V,St2} = pattern(V0, St1),
- {{map_field_exact,Line,Key,V},St2};
+ {{map_field_exact,Line,K,V},St2};
%%pattern({struct,Line,Tag,Ps}, St0) ->
%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
%% {{struct,Line,Tag,TPs},TPsvs,St1};
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 9f5be2da37..39cc03cf7a 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -80,13 +80,17 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
-type fa() :: {atom(), arity()}. % function+arity
-type ta() :: {atom(), arity()}. % type+arity
+-record(typeinfo, {attr, line}).
+
%% Usage of records, functions, and imports. The variable table, which
%% is passed on as an argument, holds the usage of variables.
-record(usage, {
calls = dict:new(), %Who calls who
imported = [], %Actually imported functions
- used_records=sets:new() :: sets:set(),%Used record definitions
- used_types = dict:new() :: dict:dict()%Used type definitions
+ used_records = sets:new() %Used record definitions
+ :: sets:set(atom()),
+ used_types = dict:new() %Used type definitions
+ :: dict:dict(ta(), line())
}).
%% Define the lint state record.
@@ -95,13 +99,17 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
-record(lint, {state=start :: 'start' | 'attribute' | 'function',
module=[], %Module
behaviour=[], %Behaviour
- exports=gb_sets:empty() :: gb_sets:set(),%Exports
- imports=[], %Imports
+ exports=gb_sets:empty() :: gb_sets:set(fa()),%Exports
+ imports=[] :: [fa()], %Imports, an orddict()
compile=[], %Compile flags
- records=dict:new() :: dict:dict(), %Record definitions
- locals=gb_sets:empty() :: gb_sets:set(),%All defined functions (prescanned)
- no_auto=gb_sets:empty() :: gb_sets:set() | 'all',%Functions explicitly not autoimported
- defined=gb_sets:empty() :: gb_sets:set(),%Defined fuctions
+ records=dict:new() %Record definitions
+ :: dict:dict(atom(), {line(),Fields :: term()}),
+ locals=gb_sets:empty() %All defined functions (prescanned)
+ :: gb_sets:set(fa()),
+ no_auto=gb_sets:empty() %Functions explicitly not autoimported
+ :: gb_sets:set(fa()) | 'all',
+ defined=gb_sets:empty() %Defined fuctions
+ :: gb_sets:set(fa()),
on_load=[] :: [fa()], %On-load function
on_load_line=0 :: line(), %Line for on_load
clashes=[], %Exported functions named as BIFs
@@ -116,12 +124,16 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
%outside any fun or lc
xqlc= false :: boolean(), %true if qlc.hrl included
new = false :: boolean(), %Has user-defined 'new/N'
- called= [] :: [{fa(),line()}], %Called functions
+ called= [] :: [{fa(),line()}], %Called functions
usage = #usage{} :: #usage{},
- specs = dict:new() :: dict:dict(), %Type specifications
- callbacks = dict:new() :: dict:dict(), %Callback types
- types = dict:new() :: dict:dict(), %Type definitions
- exp_types=gb_sets:empty():: gb_sets:set()%Exported types
+ specs = dict:new() %Type specifications
+ :: dict:dict(mfa(), line()),
+ callbacks = dict:new() %Callback types
+ :: dict:dict(mfa(), line()),
+ types = dict:new() %Type definitions
+ :: dict:dict(ta(), #typeinfo{}),
+ exp_types=gb_sets:empty() %Exported types
+ :: gb_sets:set(ta())
}).
-type lint_state() :: #lint{}.
@@ -225,6 +237,8 @@ format_error({too_many_arguments,Arity}) ->
"maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]);
%% --- patterns and guards ---
format_error(illegal_pattern) -> "illegal pattern";
+format_error(illegal_map_key) ->
+ "illegal map key";
format_error({illegal_map_key_variable,K}) ->
io_lib:format("illegal use of variable ~w in map",[K]);
format_error(illegal_bin_pattern) ->
@@ -317,10 +331,14 @@ format_error({undefined_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s undefined", [TypeName, gen_type_paren(Arity)]);
format_error({unused_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s is unused", [TypeName, gen_type_paren(Arity)]);
-format_error({new_builtin_type, {TypeName, Arity}}) ->
- io_lib:format("type ~w~s is a new builtin type; "
+%% format_error({new_builtin_type, {TypeName, Arity}}) ->
+%% io_lib:format("type ~w~s is a new builtin type; "
+%% "its (re)definition is allowed only until the next release",
+%% [TypeName, gen_type_paren(Arity)]);
+format_error({new_var_arity_type, TypeName}) ->
+ io_lib:format("type ~w is a new builtin type; "
"its (re)definition is allowed only until the next release",
- [TypeName, gen_type_paren(Arity)]);
+ [TypeName]);
format_error({builtin_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s is a builtin type; it cannot be redefined",
[TypeName, gen_type_paren(Arity)]);
@@ -344,10 +362,19 @@ format_error(spec_wrong_arity) ->
"spec has the wrong arity";
format_error(callback_wrong_arity) ->
"callback has the wrong arity";
-format_error({deprecated_type, {Name, Arity}, {Mod, NewName}, Rel}) ->
+format_error({deprecated_builtin_type, {Name, Arity},
+ Replacement, Rel}) ->
+ UseS = case Replacement of
+ {Mod, NewName} ->
+ io_lib:format("use ~w:~w/~w", [Mod, NewName, Arity]);
+ {Mod, NewName, NewArity} ->
+ io_lib:format("use ~w:~w/~w or preferably ~w:~w/~w",
+ [Mod, NewName, Arity,
+ Mod, NewName, NewArity])
+ end,
io_lib:format("type ~w/~w is deprecated and will be "
- "removed in ~s; use ~w:~w/~w",
- [Name, Arity, Rel, Mod, NewName, Arity]);
+ "removed in ~s; use ~s",
+ [Name, Arity, Rel, UseS]);
format_error({not_exported_opaque, {TypeName, Arity}}) ->
io_lib:format("opaque type ~w~s is not exported",
[TypeName, gen_type_paren(Arity)]);
@@ -499,6 +526,9 @@ start(File, Opts) ->
{deprecated_function,
bool_option(warn_deprecated_function, nowarn_deprecated_function,
true, Opts)},
+ {deprecated_type,
+ bool_option(warn_deprecated_type, nowarn_deprecated_type,
+ true, Opts)},
{obsolete_guard,
bool_option(warn_obsolete_guard, nowarn_obsolete_guard,
true, Opts)},
@@ -1016,9 +1046,10 @@ check_undefined_types(#lint{usage=Usage,types=Def}=St0) ->
Used = Usage#usage.used_types,
UTAs = dict:fetch_keys(Used),
Undef = [{TA,dict:fetch(TA, Used)} ||
- TA <- UTAs,
+ {T,_}=TA <- UTAs,
not dict:is_key(TA, Def),
- not is_default_type(TA)],
+ not is_default_type(TA),
+ not is_newly_introduced_var_arity_type(T)],
foldl(fun ({TA,L}, St) ->
add_error(L, {undefined_type,TA}, St)
end, St0, Undef).
@@ -1156,7 +1187,7 @@ export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) ->
add_error(Line, {bad_export_type, ETs}, St0)
end.
--spec exports(lint_state()) -> gb_sets:set().
+-spec exports(lint_state()) -> gb_sets:set(fa()).
exports(#lint{compile = Opts, defined = Defs, exports = Es}) ->
case lists:member(export_all, Opts) of
@@ -1373,18 +1404,20 @@ pattern({cons,_Line,H,T}, Vt, Old, Bvt, St0) ->
pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) ->
pattern_list(Ps, Vt, Old, Bvt, St);
pattern({map,_Line,Ps}, Vt, Old, Bvt, St) ->
- pattern_list(Ps, Vt, Old, Bvt, St);
-pattern({map_field_assoc,Line,_,_}, _, _, _, St) ->
- {[],[],add_error(Line, illegal_pattern, St)};
-pattern({map_field_exact,Line,KP,VP}, Vt, Old, Bvt0, St0) ->
- %% if the key pattern has variables we should fail
- case expr(KP,[],St0) of
- {[],_} ->
- pattern(VP, Vt, Old, Bvt0, St0);
- {[Var|_],_} ->
- %% found variables in key expression
- {Vt,Old,add_error(Line,{illegal_map_key_variable,element(1,Var)},St0)}
- end;
+ foldl(fun
+ ({map_field_assoc,L,_,_}, {Psvt,Bvt0,St0}) ->
+ {Psvt,Bvt0,add_error(L, illegal_pattern, St0)};
+ ({map_field_exact,L,KP,VP}, {Psvt,Bvt0,St0}) ->
+ case is_valid_map_key(KP, pattern, St0) of
+ true ->
+ {Pvt,Bvt1,St1} = pattern(VP, Vt, Old, Bvt, St0),
+ {vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt0, Bvt1), St1};
+ false ->
+ {Psvt,Bvt0,add_error(L, illegal_map_key, St0)};
+ {false,variable,Var} ->
+ {Psvt,Bvt0,add_error(L, {illegal_map_key_variable,Var}, St0)}
+ end
+ end, {[],[],St}, Ps);
%%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) ->
%% pattern_list(Ps, Vt, Old, Bvt, St);
pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) ->
@@ -1773,13 +1806,11 @@ gexpr({cons,_Line,H,T}, Vt, St) ->
gexpr({tuple,_Line,Es}, Vt, St) ->
gexpr_list(Es, Vt, St);
gexpr({map,_Line,Es}, Vt, St) ->
- gexpr_list(Es, Vt, St);
+ map_fields(Es, Vt, check_assoc_fields(Es, St), fun gexpr_list/3);
gexpr({map,_Line,Src,Es}, Vt, St) ->
- gexpr_list([Src|Es], Vt, St);
-gexpr({map_field_assoc,_Line,K,V}, Vt, St) ->
- gexpr_list([K,V], Vt, St);
-gexpr({map_field_exact,_Line,K,V}, Vt, St) ->
- gexpr_list([K,V], Vt, St);
+ {Svt,St1} = gexpr(Src, Vt, St),
+ {Fvt,St2} = map_fields(Es, Vt, St1, fun gexpr_list/3),
+ {vtmerge(Svt, Fvt),St2};
gexpr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end );
@@ -1852,6 +1883,10 @@ gexpr({op,Line,Op,A}, Vt, St0) ->
true -> {Avt,St1};
false -> {Avt,add_error(Line, illegal_guard_expr, St1)}
end;
+gexpr({op,_,'andalso',L,R}, Vt, St) ->
+ gexpr_list([L,R], Vt, St);
+gexpr({op,_,'orelse',L,R}, Vt, St) ->
+ gexpr_list([L,R], Vt, St);
gexpr({op,Line,Op,L,R}, Vt, St0) ->
{Avt,St1} = gexpr_list([L,R], Vt, St0),
case is_gexpr_op(Op, 2) of
@@ -1938,12 +1973,14 @@ is_gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, RDs) ->
is_gexpr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,F}},As}, RDs);
is_gexpr({op,_L,Op,A}, RDs) ->
is_gexpr_op(Op, 1) andalso is_gexpr(A, RDs);
+is_gexpr({op,_L,'andalso',A1,A2}, RDs) ->
+ is_gexpr_list([A1,A2], RDs);
+is_gexpr({op,_L,'orelse',A1,A2}, RDs) ->
+ is_gexpr_list([A1,A2], RDs);
is_gexpr({op,_L,Op,A1,A2}, RDs) ->
is_gexpr_op(Op, 2) andalso is_gexpr_list([A1,A2], RDs);
is_gexpr(_Other, _RDs) -> false.
-is_gexpr_op('andalso', 2) -> true;
-is_gexpr_op('orelse', 2) -> true;
is_gexpr_op(Op, A) ->
try erl_internal:op_type(Op, A) of
arith -> true;
@@ -1997,24 +2034,12 @@ expr({bc,_Line,E,Qs}, Vt, St) ->
handle_comprehension(E, Qs, Vt, St);
expr({tuple,_Line,Es}, Vt, St) ->
expr_list(Es, Vt, St);
-expr({map,Line,Es}, Vt, St) ->
- {Rvt,St1} = expr_list(Es,Vt,St),
- case is_valid_map_construction(Es) of
- true -> {Rvt,St1};
- false -> {[],add_error(Line,illegal_map_construction,St1)}
- end;
+expr({map,_Line,Es}, Vt, St) ->
+ map_fields(Es, Vt, check_assoc_fields(Es, St), fun expr_list/3);
expr({map,_Line,Src,Es}, Vt, St) ->
- expr_list([Src|Es], Vt, St);
-expr({map_field_assoc,Line,K,V}, Vt, St) ->
- case is_valid_map_key(K,St) of
- true -> expr_list([K,V], Vt, St);
- {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)}
- end;
-expr({map_field_exact,Line,K,V}, Vt, St) ->
- case is_valid_map_key(K,St) of
- true -> expr_list([K,V], Vt, St);
- {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)}
- end;
+ {Svt,St1} = expr(Src, Vt, St),
+ {Fvt,St2} = map_fields(Es, Vt, St1, fun expr_list/3),
+ {vtupdate(Svt, Fvt),St2};
expr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end);
@@ -2222,6 +2247,26 @@ record_expr(Line, Rec, Vt, St0) ->
St1 = warn_invalid_record(Line, Rec, St0),
expr(Rec, Vt, St1).
+check_assoc_fields([{map_field_exact,Line,_,_}|Fs], St) ->
+ check_assoc_fields(Fs, add_error(Line, illegal_map_construction, St));
+check_assoc_fields([{map_field_assoc,_,_,_}|Fs], St) ->
+ check_assoc_fields(Fs, St);
+check_assoc_fields([], St) ->
+ St.
+
+map_fields([{Tag,Line,K,V}|Fs], Vt, St, F) when Tag =:= map_field_assoc;
+ Tag =:= map_field_exact ->
+ St1 = case is_valid_map_key(K, St) of
+ true -> St;
+ false -> add_error(Line, illegal_map_key, St);
+ {false,variable,Var} -> add_error(Line, {illegal_map_key_variable,Var}, St)
+ end,
+ {Pvt,St2} = F([K,V], Vt, St1),
+ {Vts,St3} = map_fields(Fs, Vt, St2, F),
+ {vtupdate(Pvt, Vts),St3};
+map_fields([], Vt, St, _) ->
+ {Vt,St}.
+
%% warn_invalid_record(Line, Record, State0) -> State
%% Adds warning if the record is invalid.
@@ -2274,18 +2319,68 @@ is_valid_call(Call) ->
_ -> true
end.
-%% check_map_construction
-%% Only #{ K => V }, i.e. assoc is a valid construction
-is_valid_map_construction([{map_field_assoc,_,_,_}|Es]) ->
- is_valid_map_construction(Es);
-is_valid_map_construction([]) -> true;
-is_valid_map_construction(_) -> false.
+%% is_valid_map_key(K,St) -> true | false | {false, Var::atom()}
+%% check for value expression without variables
is_valid_map_key(K,St) ->
+ is_valid_map_key(K,expr,St).
+is_valid_map_key(K,Ctx,St) ->
case expr(K,[],St) of
- {[],_} -> true;
+ {[],_} ->
+ is_valid_map_key_value(K,Ctx);
{[Var|_],_} ->
- {false,element(1,Var)}
+ {false,variable,element(1,Var)}
+ end.
+
+is_valid_map_key_value(K,Ctx) ->
+ case K of
+ {char,_,_} -> true;
+ {integer,_,_} -> true;
+ {float,_,_} -> true;
+ {string,_,_} -> true;
+ {nil,_} -> true;
+ {atom,_,_} -> true;
+ {cons,_,H,T} ->
+ is_valid_map_key_value(H,Ctx) andalso
+ is_valid_map_key_value(T,Ctx);
+ {tuple,_,Es} ->
+ foldl(fun(E,B) ->
+ B andalso is_valid_map_key_value(E,Ctx)
+ end,true,Es);
+ {map,_,Arg,Ps} ->
+ % only check for value expressions to be valid
+ % invalid map expressions are later checked in
+ % core and kernel
+ is_valid_map_key_value(Arg,Ctx) andalso foldl(fun
+ ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
+ Tag =:= map_field_exact, Ctx =:= expr ->
+ B andalso is_valid_map_key_value(Ke,Ctx)
+ andalso is_valid_map_key_value(Ve,Ctx);
+ (_,_) -> false
+ end,true,Ps);
+ {map,_,Ps} ->
+ foldl(fun
+ ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
+ Tag =:= map_field_exact, Ctx =:= expr ->
+ B andalso is_valid_map_key_value(Ke,Ctx)
+ andalso is_valid_map_key_value(Ve,Ctx);
+ (_,_) -> false
+ end, true, Ps);
+ {record,_,_,Fs} ->
+ foldl(fun
+ ({record_field,_,Ke,Ve},B) ->
+ B andalso is_valid_map_key_value(Ke,Ctx)
+ andalso is_valid_map_key_value(Ve,Ctx)
+ end,true,Fs);
+ {bin,_,Es} ->
+ % only check for value expressions to be valid
+ % invalid binary expressions are later checked in
+ % core and kernel
+ foldl(fun
+ ({bin_element,_,E,_,_},B) ->
+ B andalso is_valid_map_key_value(E,Ctx)
+ end,true,Es);
+ _ -> false
end.
%% record_def(Line, RecordName, [RecField], State) -> State.
@@ -2500,8 +2595,6 @@ find_field(_F, []) -> error.
%% Attr :: 'type' | 'opaque'
%% Checks that a type definition is valid.
--record(typeinfo, {attr, line}).
-
type_def(_Attr, _Line, {record, _RecName}, Fields, [], St0) ->
%% The record field names and such are checked in the record format.
%% We only need to check the types.
@@ -2518,32 +2611,46 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->
CheckType = {type, -1, product, [ProtoType|Args]},
check_type(CheckType, St#lint{types=NewDefs})
end,
- case (dict:is_key(TypePair, TypeDefs) orelse is_var_arity_type(TypeName)) of
- true ->
- case is_default_type(TypePair) of
- true ->
- case is_newly_introduced_builtin_type(TypePair) of
- %% allow some types just for bootstrapping
- true ->
- Warn = {new_builtin_type, TypePair},
- St1 = add_warning(Line, Warn, St0),
- StoreType(St1);
- false ->
- add_error(Line, {builtin_type, TypePair}, St0)
- end;
- false -> add_error(Line, {redefine_type, TypePair}, St0)
- end;
- false ->
- St1 = case
- Attr =:= opaque andalso
- is_underspecified(ProtoType, Arity)
- of
- true ->
- Warn = {underspecified_opaque, TypePair},
- add_warning(Line, Warn, St0);
- false -> St0
- end,
- StoreType(St1)
+ case is_default_type(TypePair) of
+ true ->
+ case is_obsolete_builtin_type(TypePair) of
+ true -> StoreType(St0);
+ false -> add_error(Line, {builtin_type, TypePair}, St0)
+%% case is_newly_introduced_builtin_type(TypePair) of
+%% %% allow some types just for bootstrapping
+%% true ->
+%% Warn = {new_builtin_type, TypePair},
+%% St1 = add_warning(Line, Warn, St0),
+%% StoreType(St1);
+%% false ->
+%% add_error(Line, {builtin_type, TypePair}, St0)
+%% end
+ end;
+ false ->
+ case
+ dict:is_key(TypePair, TypeDefs) orelse
+ is_var_arity_type(TypeName)
+ of
+ true ->
+ case is_newly_introduced_var_arity_type(TypeName) of
+ true ->
+ Warn = {new_var_arity_type, TypeName},
+ add_warning(Line, Warn, St0);
+ false ->
+ add_error(Line, {redefine_type, TypePair}, St0)
+ end;
+ false ->
+ St1 = case
+ Attr =:= opaque andalso
+ is_underspecified(ProtoType, Arity)
+ of
+ true ->
+ Warn = {underspecified_opaque, TypePair},
+ add_warning(Line, Warn, St0);
+ false -> St0
+ end,
+ StoreType(St1)
+ end
end.
is_underspecified({type,_,term,[]}, 0) -> true;
@@ -2637,10 +2744,11 @@ check_type({type, La, TypeName, Args}, SeenVars, St) ->
St1 = case is_var_arity_type(TypeName) of
true -> St;
false ->
- Obsolete = obsolete_type(TypePair),
+ Obsolete = (is_warn_enabled(deprecated_type, St)
+ andalso obsolete_builtin_type(TypePair)),
IsObsolete =
case Obsolete of
- {deprecated, {M, _}, _} when M =/= Module ->
+ {deprecated, Repl, _} when element(1, Repl) =/= Module ->
case dict:find(TypePair, Types) of
{ok, _} -> false;
error -> true
@@ -2650,7 +2758,8 @@ check_type({type, La, TypeName, Args}, SeenVars, St) ->
case IsObsolete of
true ->
{deprecated, Replacement, Rel} = Obsolete,
- W = {deprecated_type, TypePair, Replacement, Rel},
+ Tag = deprecated_builtin_type,
+ W = {Tag, TypePair, Replacement, Rel},
add_warning(La, W, St);
false ->
OldUsed = Usage#usage.used_types,
@@ -2701,6 +2810,7 @@ check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) ->
{SeenVars, St}.
is_var_arity_type(tuple) -> true;
+is_var_arity_type(map) -> true;
is_var_arity_type(product) -> true;
is_var_arity_type(union) -> true;
is_var_arity_type(record) -> true;
@@ -2733,7 +2843,6 @@ is_default_type({iodata, 0}) -> true;
is_default_type({iolist, 0}) -> true;
is_default_type({list, 0}) -> true;
is_default_type({list, 1}) -> true;
-is_default_type({map, 0}) -> true;
is_default_type({maybe_improper_list, 0}) -> true;
is_default_type({maybe_improper_list, 2}) -> true;
is_default_type({mfa, 0}) -> true;
@@ -2764,31 +2873,32 @@ is_default_type({timeout, 0}) -> true;
is_default_type({var, 1}) -> true;
is_default_type(_) -> false.
-%% R13
-is_newly_introduced_builtin_type({arity, 0}) -> true;
-is_newly_introduced_builtin_type({array, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({bitstring, 0}) -> true;
-is_newly_introduced_builtin_type({dict, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({digraph, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({gb_set, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({gb_tree, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({iodata, 0}) -> true;
-is_newly_introduced_builtin_type({queue, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({set, 0}) -> true; % opaque
-%% R13B01
-is_newly_introduced_builtin_type({boolean, 0}) -> true;
-is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.
+is_newly_introduced_var_arity_type(map) -> true;
+is_newly_introduced_var_arity_type(_) -> false.
+
+%% is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.
+
+is_obsolete_builtin_type(TypePair) ->
+ obsolete_builtin_type(TypePair) =/= no.
%% Obsolete in OTP 17.0.
-obsolete_type({array, 0}) -> {deprecated, {array, array}, "OTP 18.0"};
-obsolete_type({dict, 0}) -> {deprecated, {dict, dict}, "OTP 18.0"};
-obsolete_type({digraph, 0}) -> {deprecated, {digraph, graph}, "OTP 18.0"};
-obsolete_type({gb_set, 0}) -> {deprecated, {gb_sets, set}, "OTP 18.0"};
-obsolete_type({gb_tree, 0}) -> {deprecated, {gb_trees, tree}, "OTP 18.0"};
-obsolete_type({queue, 0}) -> {deprecated, {queue, queue}, "OTP 18.0"};
-obsolete_type({set, 0}) -> {deprecated, {sets, set}, "OTP 18.0"};
-obsolete_type({tid, 0}) -> {deprecated, {ets, tid}, "OTP 18.0"};
-obsolete_type({Name, _}) when is_atom(Name) -> no.
+obsolete_builtin_type({array, 0}) ->
+ {deprecated, {array, array, 1}, "OTP 18.0"};
+obsolete_builtin_type({dict, 0}) ->
+ {deprecated, {dict, dict, 2}, "OTP 18.0"};
+obsolete_builtin_type({digraph, 0}) ->
+ {deprecated, {digraph, graph}, "OTP 18.0"};
+obsolete_builtin_type({gb_set, 0}) ->
+ {deprecated, {gb_sets, set, 1}, "OTP 18.0"};
+obsolete_builtin_type({gb_tree, 0}) ->
+ {deprecated, {gb_trees, tree, 2}, "OTP 18.0"};
+obsolete_builtin_type({queue, 0}) ->
+ {deprecated, {queue, queue, 1}, "OTP 18.0"};
+obsolete_builtin_type({set, 0}) ->
+ {deprecated, {sets, set, 1}, "OTP 18.0"};
+obsolete_builtin_type({tid, 0}) ->
+ {deprecated, {ets, tid}, "OTP 18.0"};
+obsolete_builtin_type({Name, A}) when is_atom(Name), is_integer(A) -> no.
%% spec_decl(Line, Fun, Types, State) -> State.
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 6316db7054..e1ae3b7aea 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -848,10 +848,12 @@ build_fun(Line, Cs) ->
end.
check_clauses(Cs, Name, Arity) ->
- mapl(fun ({clause,L,N,As,G,B}) when N =:= Name, length(As) =:= Arity ->
- {clause,L,As,G,B};
- ({clause,L,_N,_As,_G,_B}) ->
- ret_err(L, "head mismatch") end, Cs).
+ [case C of
+ {clause,L,N,As,G,B} when N =:= Name, length(As) =:= Arity ->
+ {clause,L,As,G,B};
+ {clause,L,_N,_As,_G,_B} ->
+ ret_err(L, "head mismatch")
+ end || C <- Cs].
build_try(L,Es,Scs,{Ccs,As}) ->
{'try',L,Es,Scs,Ccs,As}.
@@ -861,17 +863,6 @@ ret_err(L, S) ->
{location,Location} = get_attribute(L, location),
return_error(Location, S).
-%% mapl(F,List)
-%% an alternative map which always maps from left to right
-%% and makes it possible to interrupt the mapping with throw on
-%% the first occurence from left as expected.
-%% can be removed when the jam machine (and all other machines)
-%% uses the standardized (Erlang 5.0) evaluation order (from left to right)
-mapl(F, [H|T]) ->
- V = F(H),
- [V | mapl(F,T)];
-mapl(_, []) ->
- [].
%% Convert between the abstract form of a term and a term.
@@ -919,59 +910,63 @@ normalise_list([]) ->
Data :: term(),
AbsTerm :: abstract_expr().
abstract(T) ->
- abstract(T, 0, epp:default_encoding()).
+ abstract(T, 0, enc_func(epp:default_encoding())).
+
+-type encoding_func() :: fun((non_neg_integer()) -> boolean()).
%%% abstract/2 takes line and encoding options
-spec abstract(Data, Options) -> AbsTerm when
Data :: term(),
Options :: Line | [Option],
Option :: {line, Line} | {encoding, Encoding},
- Encoding :: latin1 | unicode | utf8,
+ Encoding :: 'latin1' | 'unicode' | 'utf8' | 'none' | encoding_func(),
Line :: erl_scan:line(),
AbsTerm :: abstract_expr().
abstract(T, Line) when is_integer(Line) ->
- abstract(T, Line, epp:default_encoding());
+ abstract(T, Line, enc_func(epp:default_encoding()));
abstract(T, Options) when is_list(Options) ->
Line = proplists:get_value(line, Options, 0),
Encoding = proplists:get_value(encoding, Options,epp:default_encoding()),
- abstract(T, Line, Encoding).
+ EncFunc = enc_func(Encoding),
+ abstract(T, Line, EncFunc).
-define(UNICODE(C),
- is_integer(C) andalso
- (C >= 0 andalso C < 16#D800 orelse
+ (C < 16#D800 orelse
C > 16#DFFF andalso C < 16#FFFE orelse
C > 16#FFFF andalso C =< 16#10FFFF)).
+enc_func(latin1) -> fun(C) -> C < 256 end;
+enc_func(unicode) -> fun(C) -> ?UNICODE(C) end;
+enc_func(utf8) -> fun(C) -> ?UNICODE(C) end;
+enc_func(none) -> none;
+enc_func(Fun) when is_function(Fun, 1) -> Fun;
+enc_func(Term) -> erlang:error({badarg, Term}).
+
abstract(T, L, _E) when is_integer(T) -> {integer,L,T};
abstract(T, L, _E) when is_float(T) -> {float,L,T};
abstract(T, L, _E) when is_atom(T) -> {atom,L,T};
abstract([], L, _E) -> {nil,L};
abstract(B, L, _E) when is_bitstring(B) ->
{bin, L, [abstract_byte(Byte, L) || Byte <- bitstring_to_list(B)]};
-abstract([C|T], L, unicode=E) when ?UNICODE(C) ->
- abstract_unicode_string(T, [C], L, E);
-abstract([C|T], L, utf8=E) when ?UNICODE(C) ->
- abstract_unicode_string(T, [C], L, E);
-abstract([C|T], L, latin1=E) when is_integer(C), 0 =< C, C < 256 ->
- abstract_string(T, [C], L, E);
-abstract([H|T], L, E) ->
+abstract([H|T], L, none=E) ->
{cons,L,abstract(H, L, E),abstract(T, L, E)};
+abstract(List, L, E) when is_list(List) ->
+ abstract_list(List, [], L, E);
abstract(Tuple, L, E) when is_tuple(Tuple) ->
- {tuple,L,abstract_list(tuple_to_list(Tuple), L, E)}.
-
-abstract_string([C|T], String, L, E) when is_integer(C), 0 =< C, C < 256 ->
- abstract_string(T, [C|String], L, E);
-abstract_string([], String, L, _E) ->
- {string, L, lists:reverse(String)};
-abstract_string(T, String, L, E) ->
- not_string(String, abstract(T, L, E), L, E).
-
-abstract_unicode_string([C|T], String, L, E) when ?UNICODE(C) ->
- abstract_unicode_string(T, [C|String], L, E);
-abstract_unicode_string([], String, L, _E) ->
+ {tuple,L,abstract_tuple_list(tuple_to_list(Tuple), L, E)}.
+
+abstract_list([H|T], String, L, E) ->
+ case is_integer(H) andalso H >= 0 andalso E(H) of
+ true ->
+ abstract_list(T, [H|String], L, E);
+ false ->
+ AbstrList = {cons,L,abstract(H, L, E),abstract(T, L, E)},
+ not_string(String, AbstrList, L, E)
+ end;
+abstract_list([], String, L, _E) ->
{string, L, lists:reverse(String)};
-abstract_unicode_string(T, String, L, E) ->
+abstract_list(T, String, L, E) ->
not_string(String, abstract(T, L, E), L, E).
not_string([C|T], Result, L, E) ->
@@ -979,9 +974,9 @@ not_string([C|T], Result, L, E) ->
not_string([], Result, _L, _E) ->
Result.
-abstract_list([H|T], L, E) ->
- [abstract(H, L, E)|abstract_list(T, L, E)];
-abstract_list([], _L, _E) ->
+abstract_tuple_list([H|T], L, E) ->
+ [abstract(H, L, E)|abstract_tuple_list(T, L, E)];
+abstract_tuple_list([], _L, _E) ->
[].
abstract_byte(Byte, L) when is_integer(Byte) ->
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index 9dbe89da91..82bc2c1460 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -256,6 +256,10 @@ ltype({type,_Line,nonempty_list,[T]}) ->
{seq,$[,$],[$,],[ltype(T),leaf("...")]};
ltype({type,Line,nil,[]}) ->
lexpr({nil,Line}, 0, options(none));
+ltype({type,Line,map,any}) ->
+ simple_type({atom,Line,map}, []);
+ltype({type,_Line,map,Pairs}) ->
+ map_type(Pairs);
ltype({type,Line,tuple,any}) ->
simple_type({atom,Line,tuple}, []);
ltype({type,_Line,tuple,Ts}) ->
@@ -289,6 +293,15 @@ binary_type(I1, I2) ->
E2 = [[leaf("_:_*"),lexpr(I2, P, options(none))] || U],
{seq,'<<','>>',[$,],E1++E2}.
+map_type(Fs) ->
+ {first,[$#],map_pair_types(Fs)}.
+
+map_pair_types(Fs) ->
+ tuple_type(Fs, fun map_pair_type/1).
+
+map_pair_type({type,_Line,map_field_assoc,Ktype,Vtype}) ->
+ {seq,[],[]," =>",[ltype(Ktype),ltype(Vtype)]}.
+
record_type(Name, Fields) ->
{first,[record_name(Name)],field_types(Fields)}.
diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl
index 40b48d7999..acf7a5cd40 100644
--- a/lib/stdlib/src/erl_tar.erl
+++ b/lib/stdlib/src/erl_tar.erl
@@ -381,7 +381,12 @@ to_octal(Int, Count, Result) ->
to_octal(Int div 8, Count-1, [Int rem 8 + $0|Result]).
to_string(Str0, Count) ->
- Str = list_to_binary(Str0),
+ Str = case file:native_name_encoding() of
+ utf8 ->
+ unicode:characters_to_binary(Str0);
+ latin1 ->
+ list_to_binary(Str0)
+ end,
case byte_size(Str) of
Size when Size < Count ->
[Str|zeroes(Count-Size)];
@@ -392,9 +397,17 @@ to_string(Str0, Count) ->
pad_file(File) ->
{ok,Position} = file:position(File, {cur,0}),
- %% There must be at least one empty record at the end of the file.
- Zeros = zeroes(?block_size - (Position rem ?block_size)),
- file:write(File, Zeros).
+ %% There must be at least two zero records at the end.
+ Fill = case ?block_size - (Position rem ?block_size) of
+ Fill0 when Fill0 < 2*?record_size ->
+ %% We need to another block here to ensure that there
+ %% are at least two zero records at the end.
+ Fill0 + ?block_size;
+ Fill0 ->
+ %% Large enough.
+ Fill0
+ end,
+ file:write(File, zeroes(Fill)).
split_filename(Name) when length(Name) =< ?th_name_len ->
{"", Name};
@@ -608,7 +621,22 @@ typeflag(Bin) ->
%% Get the name of the file from the prefix and name fields of the
%% tar header.
-get_name(Bin) ->
+get_name(Bin0) ->
+ List0 = get_name_raw(Bin0),
+ case file:native_name_encoding() of
+ utf8 ->
+ Bin = list_to_binary(List0),
+ case unicode:characters_to_list(Bin) of
+ {error,_,_} ->
+ List0;
+ List when is_list(List) ->
+ List
+ end;
+ latin1 ->
+ List0
+ end.
+
+get_name_raw(Bin) ->
Name = from_string(Bin, ?th_name, ?th_name_len),
case binary_to_list(Bin, ?th_prefix+1, ?th_prefix+1) of
[0] ->
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 35f6dff57e..6bd0eb8a22 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -59,7 +59,6 @@
file:filename()
| {file:filename(), binary()}
| {file:filename(), binary(), file:file_info()}.
--type zip_create_option() :: term().
-type section() ::
shebang
| {shebang, shebang() | default | undefined}
@@ -68,8 +67,8 @@
| {emu_args, emu_args() | undefined}
| {source, file:filename() | binary()}
| {beam, file:filename() | binary()}
- | {archive, file:filename() | binary()}
- | {archive, [zip_file()], [zip_create_option()]}.
+ | {archive, zip:filename() | binary()}
+ | {archive, [zip_file()], [zip:create_option()]}.
%%-----------------------------------------------------------------------
@@ -289,6 +288,8 @@ start(EscriptOptions) ->
my_halt(127)
end.
+-spec parse_and_run(_, _, _) -> no_return().
+
parse_and_run(File, Args, Options) ->
CheckOnly = lists:member("s", Options),
{Source, Module, FormsOrBin, HasRecs, Mode} =
@@ -727,6 +728,8 @@ epp_parse_file2(Epp, S, Forms, Parsed) ->
%% Evaluate script
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec debug(_, _, _) -> no_return().
+
debug(Module, AbsMod, Args) ->
case hidden_apply(debugger, debugger, start, []) of
{ok, _} ->
@@ -742,6 +745,8 @@ debug(Module, AbsMod, Args) ->
fatal("Cannot start the debugger")
end.
+-spec run(_, _) -> no_return().
+
run(Module, Args) ->
try
Module:main(Args),
@@ -751,6 +756,8 @@ run(Module, Args) ->
fatal(format_exception(Class, Reason))
end.
+-spec interpret(_, _, _, _) -> no_return().
+
interpret(Forms, HasRecs, File, Args) ->
%% Basic validation before execution
case erl_lint:module(Forms) of
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index a266daa084..c0921e4cf1 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -488,7 +488,7 @@ badpattern(Reason) ->
error({badpattern,Reason}).
eval_read_file_info(File, file) ->
- file:read_file_info(File);
+ file:read_link_info(File);
eval_read_file_info(File, erl_prim_loader) ->
case erl_prim_loader:read_file_info(File) of
error -> {error, erl_prim_loader};
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 7281549ea7..63116fa16e 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,7 +37,9 @@
%%-----------------------------------------------------------------
-type linkage() :: 'link' | 'nolink'.
--type emgr_name() :: {'local', atom()} | {'global', term()} | {via, atom(), term()}.
+-type emgr_name() :: {'local', atom()}
+ | {'global', term()}
+ | {'via', Module :: module(), Name :: term()}.
-type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}.
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 7629e88fbf..d39dd89d3a 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -40,6 +40,8 @@
system_continue/3,
system_terminate/4,
system_code_change/4,
+ system_get_state/1,
+ system_replace_state/2,
format_status/2]).
-export_type([handler/0, handler_args/0, add_handler_ret/0,
@@ -229,24 +231,6 @@ wake_hib(Parent, ServerName, MSL, Debug) ->
fetch_msg(Parent, ServerName, MSL, Debug, Hib) ->
receive
- {system, From, get_state} ->
- States = [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL],
- sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
- {States, [ServerName, MSL, Hib]}, Hib);
- {system, From, {replace_state, StateFun}} ->
- {NMSL, NStates} =
- lists:unzip([begin
- Cur = {Mod,Id,State},
- try
- NState = {Mod,Id,NS} = StateFun(Cur),
- {HS#handler{state=NS}, NState}
- catch
- _:_ ->
- {HS, Cur}
- end
- end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]),
- sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
- {NStates, [ServerName, NMSL, Hib]}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[ServerName, MSL, Hib],Hib);
@@ -383,6 +367,23 @@ system_code_change([ServerName, MSL, Hib], Module, OldVsn, Extra) ->
MSL),
{ok, [ServerName, MSL1, Hib]}.
+system_get_state([_ServerName, MSL, _Hib]) ->
+ {ok, [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL]}.
+
+system_replace_state(StateFun, [ServerName, MSL, Hib]) ->
+ {NMSL, NStates} =
+ lists:unzip([begin
+ Cur = {Mod,Id,State},
+ try
+ NState = {Mod,Id,NS} = StateFun(Cur),
+ {HS#handler{state=NS}, NState}
+ catch
+ _:_ ->
+ {HS, Cur}
+ end
+ end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]),
+ {ok, NStates, [ServerName, NMSL, Hib]}.
+
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
%% them, not as the real erlang messages. Use trace for that.
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index e9654322f1..e914f7d0b2 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -118,6 +118,8 @@
system_continue/3,
system_terminate/4,
system_code_change/4,
+ system_get_state/1,
+ system_replace_state/2,
format_status/2]).
-import(error_logger, [format/2]).
@@ -422,17 +424,6 @@ wake_hib(Parent, Name, StateName, StateData, Mod, Debug) ->
decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Hib) ->
case Msg of
- {system, From, get_state} ->
- Misc = [Name, StateName, StateData, Mod, Time],
- sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
- {{StateName, StateData}, Misc}, Hib);
- {system, From, {replace_state, StateFun}} ->
- State = {StateName, StateData},
- NState = {NStateName, NStateData} = try StateFun(State)
- catch _:_ -> State end,
- NMisc = [Name, NStateName, NStateData, Mod, Time],
- sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
- {NState, NMisc}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, StateName, StateData, Mod, Time], Hib);
@@ -467,6 +458,13 @@ system_code_change([Name, StateName, StateData, Mod, Time],
Else -> Else
end.
+system_get_state([_Name, StateName, StateData, _Mod, _Time]) ->
+ {ok, {StateName, StateData}}.
+
+system_replace_state(StateFun, [Name, StateName, StateData, Mod, Time]) ->
+ Result = {NStateName, NStateData} = StateFun({StateName, StateData}),
+ {ok, Result, [Name, NStateName, NStateData, Mod, Time]}.
+
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
%% them, not as the real erlang messages. Use trace for that.
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 5f14e48b0a..202a931fae 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -98,6 +98,8 @@
-export([system_continue/3,
system_terminate/4,
system_code_change/4,
+ system_get_state/1,
+ system_replace_state/2,
format_status/2]).
%% Internal exports
@@ -372,13 +374,6 @@ wake_hib(Parent, Name, State, Mod, Debug) ->
decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
case Msg of
- {system, From, get_state} ->
- sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
- {State, [Name, State, Mod, Time]}, Hib);
- {system, From, {replace_state, StateFun}} ->
- NState = try StateFun(State) catch _:_ -> State end,
- sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
- {NState, [Name, NState, Mod, Time]}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, State, Mod, Time], Hib);
@@ -687,6 +682,13 @@ system_code_change([Name, State, Mod, Time], _Module, OldVsn, Extra) ->
Else -> Else
end.
+system_get_state([_Name, State, _Mod, _Time]) ->
+ {ok, State}.
+
+system_replace_state(StateFun, [Name, State, Mod, Time]) ->
+ NState = StateFun(State),
+ {ok, NState, [Name, NState, Mod, Time]}.
+
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
%% them, not as the real erlang messages. Use trace for that.
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index b11d41e2eb..27e2a82b41 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -177,13 +177,15 @@ get_password(Io) ->
| {'expand_fun', expand_fun()}
| {'encoding', encoding()}.
--spec getopts() -> [opt_pair()].
+-spec getopts() -> [opt_pair()] | {'error', Reason} when
+ Reason :: term().
getopts() ->
getopts(default_input()).
--spec getopts(IoDevice) -> [opt_pair()] when
- IoDevice :: device().
+-spec getopts(IoDevice) -> [opt_pair()] | {'error', Reason} when
+ IoDevice :: device(),
+ Reason :: term().
getopts(Io) ->
request(Io, getopts).
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index 4057abd8d5..aece06afa6 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -25,8 +25,6 @@
-export([print/1,print/2,print/3,print/4,print/5,print/6]).
--compile(no_native).
-
%%%
%%% Exported functions
%%%
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 57b5072639..4ef1638e6d 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -23,7 +23,8 @@
fold/3,
map/2,
size/1,
- without/2
+ without/2,
+ get/3
]).
@@ -43,9 +44,6 @@
values/1
]).
--compile(no_native).
-
-%% Shadowed by erl_bif_types: maps:get/3
-spec get(Key,Map) -> Value when
Key :: term(),
Map :: map(),
@@ -54,7 +52,6 @@
get(_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:find/3
-spec find(Key,Map) -> {ok, Value} | error when
Key :: term(),
Map :: map(),
@@ -63,8 +60,8 @@ get(_,_) -> erlang:nif_error(undef).
find(_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:from_list/1
--spec from_list([{Key,Value}]) -> Map when
+-spec from_list(List) -> Map when
+ List :: [{Key,Value}],
Key :: term(),
Value :: term(),
Map :: map().
@@ -72,7 +69,6 @@ find(_,_) -> erlang:nif_error(undef).
from_list(_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:is_key/2
-spec is_key(Key,Map) -> boolean() when
Key :: term(),
Map :: map().
@@ -80,7 +76,6 @@ from_list(_) -> erlang:nif_error(undef).
is_key(_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:keys/1
-spec keys(Map) -> Keys when
Map :: map(),
Keys :: [Key],
@@ -89,7 +84,6 @@ is_key(_,_) -> erlang:nif_error(undef).
keys(_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:merge/2
-spec merge(Map1,Map2) -> Map3 when
Map1 :: map(),
Map2 :: map(),
@@ -99,14 +93,12 @@ merge(_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:new/0
-spec new() -> Map when
Map :: map().
new() -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:put/3
-spec put(Key,Value,Map1) -> Map2 when
Key :: term(),
Value :: term(),
@@ -116,7 +108,6 @@ new() -> erlang:nif_error(undef).
put(_,_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:put/3
-spec remove(Key,Map1) -> Map2 when
Key :: term(),
Map1 :: map(),
@@ -125,7 +116,6 @@ put(_,_,_) -> erlang:nif_error(undef).
remove(_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:to_list/1
-spec to_list(Map) -> [{Key,Value}] when
Map :: map(),
Key :: term(),
@@ -134,7 +124,6 @@ remove(_,_) -> erlang:nif_error(undef).
to_list(_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:update/3
-spec update(Key,Value,Map1) -> Map2 when
Key :: term(),
Value :: term(),
@@ -144,7 +133,6 @@ to_list(_) -> erlang:nif_error(undef).
update(_,_,_) -> erlang:nif_error(undef).
-%% Shadowed by erl_bif_types: maps:values/1
-spec values(Map) -> Keys when
Map :: map(),
Keys :: [Key],
@@ -155,6 +143,21 @@ values(_) -> erlang:nif_error(undef).
%%% End of BIFs
+-spec get(Key, Map, Default) -> Value | Default when
+ Key :: term(),
+ Map :: map(),
+ Value :: term(),
+ Default :: term().
+
+get(Key, Map, Default) ->
+ case maps:find(Key, Map) of
+ {ok, Value} ->
+ Value;
+ error ->
+ Default
+ end.
+
+
-spec fold(Fun,Init,Map) -> Acc when
Fun :: fun((K, V, AccIn) -> AccOut),
Init :: term(),
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index 380bc3eccc..c0ee8799c8 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -83,18 +83,18 @@ obsolete_1(crypto, sha_init, 0) ->
{deprecated, {crypto, hash_init, 1}};
obsolete_1(crypto, md4_update, 2) ->
- {deprecated, {crypto, hash_update, 3}};
+ {deprecated, {crypto, hash_update, 2}};
obsolete_1(crypto, md5_update, 2) ->
- {deprecated, {crypto, hash_update, 3}};
+ {deprecated, {crypto, hash_update, 2}};
obsolete_1(crypto, sha_update, 2) ->
- {deprecated, {crypto, hash_update, 3}};
+ {deprecated, {crypto, hash_update, 2}};
obsolete_1(crypto, md4_final, 1) ->
- {deprecated, {crypto, hash_final, 2}};
+ {deprecated, {crypto, hash_final, 1}};
obsolete_1(crypto, md5_final, 1) ->
- {deprecated, {crypto, hash_final, 2}};
+ {deprecated, {crypto, hash_final, 1}};
obsolete_1(crypto, sha_final, 1) ->
- {deprecated, {crypto, hash_final, 2}};
+ {deprecated, {crypto, hash_final, 1}};
obsolete_1(crypto, md5_mac, 2) ->
{deprecated, {crypto, hmac, 3}};
@@ -104,9 +104,9 @@ obsolete_1(crypto, sha_mac, 3) ->
{deprecated, {crypto, hmac, 4}};
obsolete_1(crypto, sha_mac_96, 2) ->
- {deprecated, {crypto, hmac_n, 3}};
+ {deprecated, {crypto, hmac, 4}};
obsolete_1(crypto, md5_mac_96, 2) ->
- {deprecated, {crypto, hmac_n, 3}};
+ {deprecated, {crypto, hmac, 4}};
obsolete_1(crypto, rsa_sign, 2) ->
{deprecated, {crypto, sign, 4}};
@@ -123,9 +123,9 @@ obsolete_1(crypto, dss_sign, 3) ->
{deprecated, {crypto, sign, 4}};
obsolete_1(crypto, dss_verify, 3) ->
- {deprecated, {crypto, verify, 4}};
+ {deprecated, {crypto, verify, 5}};
obsolete_1(crypto, dss_verify, 4) ->
- {deprecated, {crypto, verify, 4}};
+ {deprecated, {crypto, verify, 5}};
obsolete_1(crypto, mod_exp, 3) ->
{deprecated, {crypto, mod_pow, 3}};
@@ -133,7 +133,7 @@ obsolete_1(crypto, mod_exp, 3) ->
obsolete_1(crypto, dh_compute_key, 3) ->
{deprecated, {crypto, compute_key, 4}};
obsolete_1(crypto, dh_generate_key, 1) ->
- {deprecated, {crypto, generate_key, 3}};
+ {deprecated, {crypto, generate_key, 2}};
obsolete_1(crypto, dh_generate_key, 2) ->
{deprecated, {crypto, generate_key, 3}};
@@ -250,12 +250,12 @@ obsolete_1(snmp, N, A) ->
false ->
no;
true ->
- {deprecated, "Deprecated (will be removed in R17B); use snmpa:"++atom_to_list(N)++"/"++
+ {deprecated, "Deprecated (will be removed in OTP 18); use snmpa:"++atom_to_list(N)++"/"++
integer_to_list(A)++" instead"}
end;
obsolete_1(snmpa, old_info_format, 1) ->
- {deprecated, "Deprecated; (will be removed in R17B); use \"new\" format instead"};
+ {deprecated, "Deprecated; (will be removed in OTP 18); use \"new\" format instead"};
obsolete_1(snmpm, agent_info, 3) ->
{removed, {snmpm, agent_info, 2}, "R16B"};
obsolete_1(snmpm, update_agent_info, 5) ->
@@ -366,23 +366,6 @@ obsolete_1(auth, node_cookie, 1) ->
obsolete_1(auth, node_cookie, 2) ->
{deprecated, "Deprecated; use erlang:set_cookie/2 and net_adm:ping/1 instead"};
-obsolete_1(erlang, is_constant, 1) ->
- {removed, "Removed in R13B"};
-
-%% Added in R12B-0.
-obsolete_1(ssl, port, 1) ->
- {removed, {ssl, sockname, 1}, "R13B"};
-obsolete_1(ssl, accept, A) when A =:= 1; A =:= 2 ->
- {removed, "deprecated; use ssl:transport_accept/1,2 and ssl:ssl_accept/1,2"};
-obsolete_1(erlang, fault, 1) ->
- {removed, {erlang,error,1}, "R13B"};
-obsolete_1(erlang, fault, 2) ->
- {removed, {erlang,error,2}, "R13B"};
-
-%% Added in R12B-2.
-obsolete_1(file, rawopen, 2) ->
- {removed, "deprecated (will be removed in R13B); use file:open/2 with the raw option"};
-
obsolete_1(http, request, 1) -> {removed,{httpc,request,1},"R15B"};
obsolete_1(http, request, 2) -> {removed,{httpc,request,2},"R15B"};
obsolete_1(http, request, 4) -> {removed,{httpc,request,4},"R15B"};
@@ -524,7 +507,7 @@ obsolete_1(docb_xml_check, _, _) ->
%% Added in R15B
obsolete_1(asn1rt, F, _) when F == load_driver; F == unload_driver ->
- {deprecated,"deprecated (will be removed in R16A); has no effect as drivers are no longer used."};
+ {deprecated,"deprecated (will be removed in OTP 18); has no effect as drivers are no longer used"};
obsolete_1(ssl, pid, 1) ->
{removed,"was removed in R16; is no longer needed"};
obsolete_1(inviso, _, _) ->
@@ -532,7 +515,7 @@ obsolete_1(inviso, _, _) ->
%% Added in R15B01.
obsolete_1(gs, _, _) ->
- {deprecated,"the gs application has been deprecated and will be removed in R17; use the wx application instead"};
+ {deprecated,"the gs application has been deprecated and will be removed in OTP 18; use the wx application instead"};
obsolete_1(ssh, sign_data, 2) ->
{deprecated,"deprecated (will be removed in R16A); use public_key:pem_decode/1, public_key:pem_entry_decode/1 "
"and public_key:sign/3 instead"};
@@ -594,6 +577,8 @@ obsolete_1(asn1rt, utf8_binary_to_list, 1) ->
{deprecated,{unicode,characters_to_list,1}};
obsolete_1(asn1rt, utf8_list_to_binary, 1) ->
{deprecated,{unicode,characters_to_binary,1}};
+obsolete_1(pg, _, _) ->
+ {deprecated,"deprecated; will be removed in OTP 18"};
obsolete_1(_, _, _) ->
no.
diff --git a/lib/stdlib/src/pg.erl b/lib/stdlib/src/pg.erl
index ee177e4e0b..a41fd329c2 100644
--- a/lib/stdlib/src/pg.erl
+++ b/lib/stdlib/src/pg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,6 +17,7 @@
%% %CopyrightEnd%
%%
-module(pg).
+-deprecated(module).
%% pg provides a process group facility. Messages
%% can be multicasted to all members in the group
diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl
index c26764eb18..b6bb758dfb 100644
--- a/lib/stdlib/src/qlc_pt.erl
+++ b/lib/stdlib/src/qlc_pt.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1218,13 +1218,14 @@ lu_skip(ColConstants, FilterData, PatternFrame, PatternVars,
%% column, the filter will not be skipped.
%% (an example: {X=1} <- ..., X =:= 1).
length(D = Cols -- PatternColumns) =:= 1,
- Frame <- SFs,
- begin
+ {{_,Col} = Column, Constants} <- D,
+ %% Check that the following holds for all frames.
+ lists:all(
+ fun(Frame) ->
%% The column is compared/matched against a constant.
%% If there are no more comparisons/matches then
%% the filter can be replaced by the lookup of
%% the constant.
- [{{_,Col} = Column, Constants}] = D,
{VarI, FrameI} = unify_column(Frame, PV, Col, BindFun,
Imported),
VarValues = deref_skip(VarI, FrameI, LookupOp, Imported),
@@ -1253,7 +1254,7 @@ lu_skip(ColConstants, FilterData, PatternFrame, PatternVars,
length(VarValues) =< 1 andalso
(Constants -- LookedUpConstants =:= []) andalso
bindings_is_subset(Frame, F2, Imported)
- end],
+ end, SFs)],
ColFils = family_list(ColFil),
%% The skip tag 'all' means that all filters are covered by the lookup.
%% It does not imply that there is only one generator as is the case
diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl
index be4b600f25..167a676281 100644
--- a/lib/stdlib/src/sets.erl
+++ b/lib/stdlib/src/sets.erl
@@ -55,9 +55,8 @@
%%------------------------------------------------------------------------------
--type seg() :: tuple().
--type segs(E) :: tuple()
- | E. % dummy
+-type seg() :: tuple().
+-type segs(_Element) :: tuple().
%% Define a hash set. The default values are the standard ones.
-record(set,
diff --git a/lib/stdlib/src/slave.erl b/lib/stdlib/src/slave.erl
index 3e647635bc..1898dc8aba 100644
--- a/lib/stdlib/src/slave.erl
+++ b/lib/stdlib/src/slave.erl
@@ -290,7 +290,10 @@ register_unique_name(Number) ->
%% no need to use rsh.
mk_cmd(Host, Name, Args, Waiter, Prog0) ->
- Prog = quote_progname(Prog0),
+ Prog = case os:type() of
+ {ose,_} -> mk_ose_prog(Prog0);
+ _ -> quote_progname(Prog0)
+ end,
BasicCmd = lists:concat([Prog,
" -detached -noinput -master ", node(),
" ", long_or_short(), Name, "@", Host,
@@ -310,6 +313,24 @@ mk_cmd(Host, Name, Args, Waiter, Prog0) ->
end
end.
+%% On OSE we have to pass the beam arguments directory to the slave
+%% process. To find out what arguments that should be passed on we
+%% make an assumption. All arguments after the last "--" should be
+%% skipped. So given these arguments:
+%% -Muycs256 -A 1 -- -root /mst/ -progname beam.debug.smp -- -home /mst/ -- -kernel inetrc '"/mst/inetrc.conf"' -- -name test@localhost
+%% we send
+%% -Muycs256 -A 1 -- -root /mst/ -progname beam.debug.smp -- -home /mst/ -- -kernel inetrc '"/mst/inetrc.conf"' --
+%% to the slave with whatever other args that are added in mk_cmd.
+mk_ose_prog(Prog) ->
+ SkipTail = fun("--",[]) ->
+ ["--"];
+ (_,[]) ->
+ [];
+ (Arg,Args) ->
+ [Arg," "|Args]
+ end,
+ [Prog,tl(lists:foldr(SkipTail,[],erlang:system_info(emu_args)))].
+
%% This is an attempt to distinguish between spaces in the program
%% path and spaces that separate arguments. The program is quoted to
%% allow spaces in the path.
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index a64b8e13c0..d388410de0 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -102,5 +102,8 @@
{registered,[timer_server,rsh_starter,take_over_monitor,pool_master,
dets]},
{applications, [kernel]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["sasl-2.4","kernel-3.0","erts-6.0","crypto-3.3",
+ "compiler-5.0"]}
+]}.
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index 04f8dfb61b..e25cc25f57 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -102,20 +102,31 @@ get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout).
-spec get_state(Name) -> State when
Name :: name(),
State :: term().
-get_state(Name) -> send_system_msg(Name, get_state).
+get_state(Name) ->
+ case send_system_msg(Name, get_state) of
+ {error, Reason} -> error(Reason);
+ State -> State
+ end.
-spec get_state(Name, Timeout) -> State when
Name :: name(),
Timeout :: timeout(),
State :: term().
-get_state(Name, Timeout) -> send_system_msg(Name, get_state, Timeout).
+get_state(Name, Timeout) ->
+ case send_system_msg(Name, get_state, Timeout) of
+ {error, Reason} -> error(Reason);
+ State -> State
+ end.
-spec replace_state(Name, StateFun) -> NewState when
Name :: name(),
StateFun :: fun((State :: term()) -> NewState :: term()),
NewState :: term().
replace_state(Name, StateFun) ->
- send_system_msg(Name, {replace_state, StateFun}).
+ case send_system_msg(Name, {replace_state, StateFun}) of
+ {error, Reason} -> error(Reason);
+ State -> State
+ end.
-spec replace_state(Name, StateFun, Timeout) -> NewState when
Name :: name(),
@@ -123,7 +134,10 @@ replace_state(Name, StateFun) ->
Timeout :: timeout(),
NewState :: term().
replace_state(Name, StateFun, Timeout) ->
- send_system_msg(Name, {replace_state, StateFun}, Timeout).
+ case send_system_msg(Name, {replace_state, StateFun}, Timeout) of
+ {error, Reason} -> error(Reason);
+ State -> State
+ end.
-spec change_code(Name, Module, OldVsn, Extra) -> 'ok' | {error, Reason} when
Name :: name(),
@@ -390,10 +404,11 @@ do_cmd(_, suspend, _Parent, _Mod, Debug, Misc) ->
{suspended, ok, Debug, Misc};
do_cmd(_, resume, _Parent, _Mod, Debug, Misc) ->
{running, ok, Debug, Misc};
-do_cmd(SysState, get_state, _Parent, _Mod, Debug, {State, Misc}) ->
- {SysState, State, Debug, Misc};
-do_cmd(SysState, replace_state, _Parent, _Mod, Debug, {State, Misc}) ->
- {SysState, State, Debug, Misc};
+do_cmd(SysState, get_state, _Parent, Mod, Debug, Misc) ->
+ {SysState, do_get_state(Mod, Misc), Debug, Misc};
+do_cmd(SysState, {replace_state, StateFun}, _Parent, Mod, Debug, Misc) ->
+ {Res, NMisc} = do_replace_state(StateFun, Mod, Misc),
+ {SysState, Res, Debug, NMisc};
do_cmd(SysState, get_status, Parent, Mod, Debug, Misc) ->
Res = get_status(SysState, Parent, Mod, Debug, Misc),
{SysState, Res, Debug, Misc};
@@ -407,6 +422,40 @@ do_cmd(suspended, {change_code, Module, Vsn, Extra}, _Parent,
do_cmd(SysState, Other, _Parent, _Mod, Debug, Misc) ->
{SysState, {error, {unknown_system_msg, Other}}, Debug, Misc}.
+do_get_state(Mod, Misc) ->
+ case erlang:function_exported(Mod, system_get_state, 1) of
+ true ->
+ try
+ {ok, State} = Mod:system_get_state(Misc),
+ State
+ catch
+ Cl:Exc ->
+ {error, {callback_failed,{Mod,system_get_state},{Cl,Exc}}}
+ end;
+ false ->
+ Misc
+ end.
+
+do_replace_state(StateFun, Mod, Misc) ->
+ case erlang:function_exported(Mod, system_replace_state, 2) of
+ true ->
+ try
+ {ok, State, NMisc} = Mod:system_replace_state(StateFun, Misc),
+ {State, NMisc}
+ catch
+ Cl:Exc ->
+ {{error, {callback_failed,{Mod,system_replace_state},{Cl,Exc}}}, Misc}
+ end;
+ false ->
+ try
+ NMisc = StateFun(Misc),
+ {NMisc, NMisc}
+ catch
+ Cl:Exc ->
+ {{error, {callback_failed,StateFun,{Cl,Exc}}}, Misc}
+ end
+ end.
+
get_status(SysState, Parent, Mod, Debug, Misc) ->
PDict = get(),
FmtMisc =
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index c40ce8e203..b768c6d0b9 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -203,8 +203,18 @@
zip_comment_length}).
--type zip_file() :: #zip_file{}.
+-type create_option() :: memory | cooked | verbose | {comment, string()}
+ | {cwd, file:filename()}
+ | {compress, extension_spec()}
+ | {uncompress, extension_spec()}.
+-type extension() :: string().
+-type extension_spec() :: all | [extension()] | {add, [extension()]} | {del, [extension()]}.
+-type filename() :: file:filename().
+
-type zip_comment() :: #zip_comment{}.
+-type zip_file() :: #zip_file{}.
+
+-export_type([create_option/0, filename/0]).
%% Open a zip archive with options
%%
@@ -340,13 +350,13 @@ unzip(F) -> unzip(F, []).
-spec(unzip(Archive, Options) -> RetValue when
Archive :: file:name() | binary(),
Options :: [Option],
- Option :: {file_list, FileList}
+ Option :: {file_list, FileList} | cooked
| keep_old_files | verbose | memory |
{file_filter, FileFilter} | {cwd, CWD},
FileList :: [file:name()],
FileBinList :: [{file:name(),binary()}],
FileFilter :: fun((ZipFile) -> boolean()),
- CWD :: string(),
+ CWD :: file:filename(),
ZipFile :: zip_file(),
RetValue :: {ok, FileList}
| {ok, FileBinList}
@@ -430,7 +440,7 @@ zip(F, Files) -> zip(F, Files, []).
What :: all | [Extension] | {add, [Extension]} | {del, [Extension]},
Extension :: string(),
Comment :: string(),
- CWD :: string(),
+ CWD :: file:filename(),
RetValue :: {ok, FileName :: file:name()}
| {ok, {FileName :: file:name(), binary()}}
| {error, Reason :: term()}).
@@ -712,8 +722,8 @@ table(F, O) -> list_dir(F, O).
FileList :: [FileSpec],
FileSpec :: file:name() | {file:name(), binary()}
| {file:name(), binary(), file:file_info()},
- RetValue :: {ok, FileName :: file:name()}
- | {ok, {FileName :: file:name(), binary()}}
+ RetValue :: {ok, FileName :: filename()}
+ | {ok, {FileName :: filename(), binary()}}
| {error, Reason :: term()}).
create(F, Fs) -> zip(F, Fs).
@@ -724,14 +734,9 @@ create(F, Fs) -> zip(F, Fs).
FileSpec :: file:name() | {file:name(), binary()}
| {file:name(), binary(), file:file_info()},
Options :: [Option],
- Option :: memory | cooked | verbose | {comment, Comment}
- | {cwd, CWD} | {compress, What} | {uncompress, What},
- What :: all | [Extension] | {add, [Extension]} | {del, [Extension]},
- Extension :: string(),
- Comment :: string(),
- CWD :: string(),
- RetValue :: {ok, FileName :: file:name()}
- | {ok, {FileName :: file:name(), binary()}}
+ Option :: create_option(),
+ RetValue :: {ok, FileName :: filename()}
+ | {ok, {FileName :: filename(), binary()}}
| {error, Reason :: term()}).
create(F, Fs, O) -> zip(F, Fs, O).
@@ -755,7 +760,7 @@ extract(F) -> unzip(F).
FileList :: [file:name()],
FileBinList :: [{file:name(),binary()}],
FileFilter :: fun((ZipFile) -> boolean()),
- CWD :: string(),
+ CWD :: file:filename(),
ZipFile :: zip_file(),
RetValue :: {ok, FileList}
| {ok, FileBinList}
@@ -1153,7 +1158,7 @@ zip_open(Archive) -> zip_open(Archive, []).
Archive :: file:name() | binary(),
ZipHandle :: pid(),
Options :: [Option],
- Option :: cooked | memory | {cwd, CWD :: string()},
+ Option :: cooked | memory | {cwd, CWD :: file:filename()},
Reason :: term()).
zip_open(Archive, Options) ->
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index af82f22b21..a271229c59 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -73,6 +73,8 @@ MODULES= \
supervisor_SUITE \
supervisor_bridge_SUITE \
sys_SUITE \
+ sys_sp1 \
+ sys_sp2 \
tar_SUITE \
timer_SUITE \
timer_simple_SUITE \
@@ -83,7 +85,8 @@ MODULES= \
zip_SUITE \
random_unicode_list \
random_iolist \
- error_logger_forwarder
+ error_logger_forwarder \
+ maps_SUITE
ERL_FILES= $(MODULES:%=%.erl)
diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl
index d5a0fe21b4..32cec0db6f 100644
--- a/lib/stdlib/test/binary_module_SUITE.erl
+++ b/lib/stdlib/test/binary_module_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -40,7 +40,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
% Default timetrap timeout (set in init_per_testcase).
% Some of these testcases are really heavy...
--define(default_timeout, ?t:minutes(20)).
+-define(default_timeout, ?t:minutes(30)).
-endif.
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index 059d553b00..6be37cbecf 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -52,7 +52,7 @@
simultaneous_open/1, insert_new/1, repair_continuation/1,
otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1,
otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1,
- otp_8923/1, otp_9282/1, otp_11245/1]).
+ otp_8923/1, otp_9282/1, otp_11245/1, otp_11709/1]).
-export([dets_dirty_loop/0]).
@@ -109,7 +109,7 @@ all() ->
many_clients, otp_4906, otp_5402, simultaneous_open,
insert_new, repair_continuation, otp_5487, otp_6206,
otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898,
- otp_8899, otp_8903, otp_8923, otp_9282, otp_11245
+ otp_8899, otp_8903, otp_8923, otp_9282, otp_11245, otp_11709
].
groups() ->
@@ -772,9 +772,9 @@ open_1(Config, V) ->
crash(Fname, TypePos),
{error, {invalid_type_code,Fname}} = dets:open_file(Fname),
truncate(Fname, HeadSize - 10),
- {error, {tooshort,Fname}} = dets:open_file(Fname),
- {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
- ok = dets:close(TabRef),
+ {error,{not_a_dets_file,Fname}} = dets:open_file(Fname),
+ {error,{not_a_dets_file,Fname}} =
+ dets:open_file(TabRef, [{file,Fname},{version,V}]),
file:delete(Fname),
{error,{file_error,{foo,bar},_}} = dets:is_dets_file({foo,bar}),
@@ -967,10 +967,12 @@ fast_init_table(Config) ->
{'EXIT', _} =
(catch dets:init_table(TabRef, fun(foo) -> bar end, {format,bchunk})),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
{'EXIT', _} = (catch dets:init_table(TabRef, fun() -> foo end,
{format,bchunk})),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
{'EXIT', {badarg, _}} =
(catch dets:init_table(TabRef, nofun, {format,bchunk})),
@@ -979,10 +981,12 @@ fast_init_table(Config) ->
away = (catch dets:init_table(TabRef, fun(_) -> throw(away) end,
{format,bchunk})),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
{error, {init_fun, fopp}} =
dets:init_table(TabRef, fun(read) -> fopp end, {format,bchunk}),
dets:close(TabRef),
+ file:delete(Fname),
{ok, _} = dets:open_file(TabRef, Args),
dets:safe_fixtable(TabRef, true),
{error, {fixed_table, TabRef}} =
@@ -1389,23 +1393,6 @@ repair(Config, V) ->
{ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
ok = ins(TabRef, 100),
ok = dets:close(TabRef),
- truncate(Fname, HeadSize - 10),
- %% a new file is created ('tooshort')
- {ok, TabRef} = dets:open_file(TabRef,
- [{file,Fname},{version,V},
- {min_no_slots,1000},
- {max_no_slots,1000000}]),
- case dets:info(TabRef, no_slots) of
- undefined -> ok;
- {Min1,Slot1,Max1} ->
- true = Min1 =< Slot1, true = Slot1 =< Max1,
- true = 1000 < Min1, true = 1000+256 > Min1,
- true = 1000000 < Max1, true = (1 bsl 20)+256 > Max1
- end,
- 0 = dets:info(TabRef, size),
- no_keys_test(TabRef),
- _ = histogram(TabRef, silent),
- ok = dets:close(TabRef),
file:delete(Fname),
%% version bump (v8)
@@ -3920,19 +3907,52 @@ otp_11245(Config) when is_list(Config) ->
file:delete(File),
ok.
+otp_11709(doc) ->
+ ["OTP-11709. Bugfixes."];
+otp_11709(suite) ->
+ [];
+otp_11709(Config) when is_list(Config) ->
+ Short = <<"foo">>,
+ Long = <<"a sufficiently long text">>,
+
+ %% Bug: leaking file descriptor
+ P0 = pps(),
+ File = filename(otp_11709, Config),
+ ok = file:write_file(File, Long),
+ false = dets:is_dets_file(File),
+ check_pps(P0),
+
+ %% Bug: deleting file
+ Args = [[{access, A}, {repair, R}] ||
+ A <- [read, read_write],
+ R <- [true, false, force]],
+ Fun1 = fun(S, As) ->
+ P1 = pps(),
+ ok = file:write_file(File, S),
+ {error,{not_a_dets_file,File}} = dets:open_file(File, As),
+ {ok, S} = file:read_file(File),
+ check_pps(P1)
+ end,
+ Fun2 = fun(S) ->
+ _ = [Fun1(S, As) || As <- Args],
+ ok
+ end,
+ ok = Fun2(Long), % no change here
+ ok = Fun2(Short), % mimic the behaviour for longer files
+
+ %% open_file/1
+ ok = file:write_file(File, Long),
+ {error,{not_a_dets_file,File}} = dets:open_file(File), % no change
+ ok = file:write_file(File, Short),
+ {error,{not_a_dets_file,File}} = dets:open_file(File), % mimic
+
+ _ = file:delete(File),
+ ok.
+
%%
%% Parts common to several test cases
%%
-start_node_rel(Name, Rel, How) ->
- Release = [{release, atom_to_list(Rel)}],
- Pa = filename:dirname(code:which(?MODULE)),
- test_server:start_node(Name, How,
- [{args,
- " -kernel net_setuptime 100 "
- " -pa " ++ Pa},
- {erl, Release}]).
-
crash(File, Where) ->
crash(File, Where, 10).
@@ -4323,7 +4343,7 @@ check_badarg({'EXIT', {badarg, [{M,F,Args,_} | _]}}, M, F, Args) ->
check_badarg({'EXIT', {badarg, [{M,F,A,_} | _]}}, M, F, Args) ->
true = test_server:is_native(M) andalso length(Args) =:= A.
-check_pps(P0) ->
+check_pps({Ports0,Procs0} = P0) ->
case pps() of
P0 ->
ok;
@@ -4335,22 +4355,28 @@ check_pps(P0) ->
case pps() of
P0 ->
ok;
- P1 ->
- io:format("failure, got ~p~n, expected ~p\n", [P1, P0]),
- {Ports0,Procs0} = P0,
- {Ports1,Procs1} = P1,
- show("Old ports", Ports0 -- Ports1),
- show("New ports", Ports1 -- Ports0),
- show("Old procs", Procs0 -- Procs1),
- show("New procs", Procs1 -- Procs0),
- ?t:fail()
- end
+ {Ports1,Procs1} = P1 ->
+ case {Ports1 -- Ports0, Procs1 -- Procs0} of
+ {[], []} -> ok;
+ {PortsDiff,ProcsDiff} ->
+ io:format("failure, got ~p~n, expected ~p\n", [P1, P0]),
+ show("Old port", Ports0 -- Ports1),
+ show("New port", PortsDiff),
+ show("Old proc", Procs0 -- Procs1),
+ show("New proc", ProcsDiff),
+ ?t:fail()
+ end
+ end
end.
show(_S, []) ->
ok;
-show(S, L) ->
- io:format("~s: ~p~n", [S, L]).
+show(S, [Pid|Pids]) when is_pid(Pid) ->
+ io:format("~s: ~p~n", [S, erlang:process_info(Pid)]),
+ show(S, Pids);
+show(S, [Port|Ports]) when is_port(Port)->
+ io:format("~s: ~p~n", [S, erlang:port_info(Port)]),
+ show(S, Ports).
pps() ->
dets:start(),
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index 0b4726c07a..b17e8bd186 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -26,7 +26,7 @@
pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1,
otp_8130/1, overload_mac/1, otp_8388/1, otp_8470/1, otp_8503/1,
otp_8562/1, otp_8665/1, otp_8911/1, otp_10302/1, otp_10820/1,
- otp_11728/1]).
+ otp_11728/1, encoding/1]).
-export([epp_parse_erl_form/2]).
@@ -68,7 +68,8 @@ all() ->
{group, variable}, otp_4870, otp_4871, otp_5362, pmod,
not_circular, skip_header, otp_6277, otp_7702, otp_8130,
overload_mac, otp_8388, otp_8470, otp_8503, otp_8562,
- otp_8665, otp_8911, otp_10302, otp_10820, otp_11728].
+ otp_8665, otp_8911, otp_10302, otp_10820, otp_11728,
+ encoding].
groups() ->
[{upcase_mac, [], [upcase_mac_1, upcase_mac_2]},
@@ -123,10 +124,22 @@ include_local(Config) when is_list(Config) ->
%%% regular epp:parse_file, the test case will time out, and then epp
%%% server will go on growing until we dump core.
epp_parse_file(File, Inc, Predef) ->
- {ok, Epp} = epp:open(File, Inc, Predef),
+ List = do_epp_parse_file(fun() ->
+ epp:open(File, Inc, Predef)
+ end),
+ List = do_epp_parse_file(fun() ->
+ Opts = [{name, File},
+ {includes, Inc},
+ {macros, Predef}],
+ epp:open(Opts)
+ end),
+ {ok, List}.
+
+do_epp_parse_file(Open) ->
+ {ok, Epp} = Open(),
List = collect_epp_forms(Epp),
epp:close(Epp),
- {ok, List}.
+ List.
collect_epp_forms(Epp) ->
Result = epp_parse_erl_form(Epp),
@@ -1413,6 +1426,63 @@ otp_11728(Config) when is_list(Config) ->
_ = file:delete(ErlFile),
ok.
+%% Check the new API for setting the default encoding.
+encoding(Config) when is_list(Config) ->
+ Dir = ?config(priv_dir, Config),
+ ErlFile = filename:join(Dir, "encoding.erl"),
+
+ %% Try a latin-1 file with no encoding given.
+ C1 = <<"-module(encoding).
+ %% ",246,"
+ ">>,
+ ok = file:write_file(ErlFile, C1),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {error,_},
+ {error,{2,epp,cannot_parse}},
+ {eof,2}]} = epp:parse_file(ErlFile, []),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,3}]} =
+ epp:parse_file(ErlFile, [{default_encoding,latin1}]),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,3}],[{encoding,none}]} =
+ epp:parse_file(ErlFile, [{default_encoding,latin1},extra]),
+
+ %% Try a latin-1 file with encoding given in a comment.
+ C2 = <<"-module(encoding).
+ %% encoding: latin-1
+ %% ",246,"
+ ">>,
+ ok = file:write_file(ErlFile, C2),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}]} =
+ epp:parse_file(ErlFile, []),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}]} =
+ epp:parse_file(ErlFile, [{default_encoding,latin1}]),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}]} =
+ epp:parse_file(ErlFile, [{default_encoding,utf8}]),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}],[{encoding,latin1}]} =
+ epp:parse_file(ErlFile, [extra]),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}],[{encoding,latin1}]} =
+ epp:parse_file(ErlFile, [{default_encoding,latin1},extra]),
+ {ok,[{attribute,1,file,_},
+ {attribute,1,module,encoding},
+ {eof,4}],[{encoding,latin1}]} =
+ epp:parse_file(ErlFile, [{default_encoding,utf8},extra]),
+ ok.
+
+
check(Config, Tests) ->
eval_tests(Config, fun check_test/2, Tests).
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index e6512b7d71..b55324161b 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -1451,6 +1451,15 @@ eep43(Config) when is_list(Config) ->
" {Map#{a := B},Map#{a => c},Map#{d => e}} "
"end.",
{#{a => b},#{a => c},#{a => b,d => e}}),
+ check(fun () ->
+ lists:map(fun (X) -> X#{price := 0} end,
+ [#{hello => 0, price => nil}])
+ end,
+ "lists:map(fun (X) -> X#{price := 0} end,
+ [#{hello => 0, price => nil}]).",
+ [#{hello => 0, price => 0}]),
+ error_check("[camembert]#{}.", {badarg,[camembert]}),
+ error_check("#{} = 1.", {badmatch,1}),
ok.
%% Check the string in different contexts: as is; in fun; from compiled code.
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 1614a2722f..ea61b2082b 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -52,6 +52,7 @@
guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1,
otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1,
otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1, otp_11254/1,
+ otp_11772/1, otp_11771/1, otp_11872/1,
export_all/1,
bif_clash/1,
behaviour_basic/1, behaviour_multiple/1,
@@ -61,7 +62,8 @@
on_load_successful/1, on_load_failing/1,
too_many_arguments/1,
basic_errors/1,bin_syntax_errors/1,
- predef/1
+ predef/1,
+ maps/1,maps_type/1
]).
% Default timetrap timeout (set in init_per_testcase).
@@ -85,10 +87,12 @@ all() ->
unsized_binary_in_bin_gen_pattern,
otp_4886, otp_4988, otp_5091, otp_5276, otp_5338,
otp_5362, otp_5371, otp_7227, otp_5494, otp_5644,
- otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, otp_11254,export_all,
+ otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, otp_11254,
+ otp_11772, otp_11771, otp_11872, export_all,
bif_clash, behaviour_basic, behaviour_multiple,
otp_7550, otp_8051, format_warn, {group, on_load},
- too_many_arguments, basic_errors, bin_syntax_errors, predef].
+ too_many_arguments, basic_errors, bin_syntax_errors, predef,
+ maps, maps_type].
groups() ->
[{unused_vars_warn, [],
@@ -1275,10 +1279,9 @@ guard(Config) when is_list(Config) ->
tuple.
">>,
[nowarn_obsolete_guard],
- {error,
+ {errors,
[{6,erl_lint,illegal_guard_expr},{18,erl_lint,illegal_guard_expr}],
- [{18,erl_lint,{removed,{erlang,is_constant,1},
- "Removed in R13B"}}]}},
+ []}},
{guard2,
<<"-record(apa,{}).
t1(A) when atom(A), atom(A) ->
@@ -1337,14 +1340,11 @@ guard(Config) when is_list(Config) ->
tuple.
">>,
[nowarn_obsolete_guard],
- {error,[{6,erl_lint,illegal_guard_expr},
- {6,erl_lint,illegal_guard_expr},
- {18,erl_lint,illegal_guard_expr},
- {18,erl_lint,illegal_guard_expr}],
- [{18,erl_lint,{removed,{erlang,is_constant,1},
- "Removed in R13B"}},
- {18,erl_lint,{removed,{erlang,is_constant,1},
- "Removed in R13B"}}]}},
+ {errors,[{6,erl_lint,illegal_guard_expr},
+ {6,erl_lint,illegal_guard_expr},
+ {18,erl_lint,illegal_guard_expr},
+ {18,erl_lint,illegal_guard_expr}],
+ []}},
{guard3,
<<"-record(apa,{}).
t2(A) when atom(A); atom(A) ->
@@ -1494,7 +1494,15 @@ guard(Config) when is_list(Config) ->
[],
{errors,[{1,erl_lint,illegal_guard_expr},
{2,erl_lint,illegal_guard_expr},
- {3,erl_lint,illegal_guard_expr}],[]}}
+ {3,erl_lint,illegal_guard_expr}],[]}},
+ {guard9,
+ <<"t(X, Y) when erlang:'andalso'(X, Y) -> ok;
+ t(X, Y) when erlang:'orelse'(X, Y) -> ok.
+ ">>,
+ [],
+ {errors,[{1,erl_lint,illegal_guard_expr},
+ {2,erl_lint,illegal_guard_expr}],
+ []}}
],
?line [] = run(Config, Ts1),
ok.
@@ -2553,7 +2561,7 @@ otp_10436(Config) when is_list(Config) ->
ok.
otp_11254(doc) ->
- "OTP-11254. Warnings for opaque types.";
+ "OTP-11254. M:F/A could crash the linter.";
otp_11254(suite) -> [];
otp_11254(Config) when is_list(Config) ->
Ts = <<"-module(p2).
@@ -2566,6 +2574,85 @@ otp_11254(Config) when is_list(Config) ->
run_test2(Config, Ts, []),
ok.
+otp_11772(doc) ->
+ "OTP-11772. Reintroduce errors for redefined builtin types.";
+otp_11772(suite) -> [];
+otp_11772(Config) when is_list(Config) ->
+ Ts = <<"
+ -module(newly).
+
+ -compile(export_all).
+
+ %% Built-in:
+ -type node() :: node().
+ -type mfa() :: tuple().
+ -type gb_tree() :: mfa(). % Allowed since Erlang/OTP 17.0
+ -type digraph() :: [_]. % Allowed since Erlang/OTP 17.0
+
+ -type t() :: mfa() | digraph() | gb_tree() | node().
+
+ -spec t() -> t().
+
+ t() ->
+ 1.
+ ">>,
+ {errors,[{7,erl_lint,{builtin_type,{node,0}}},
+ {8,erl_lint,{builtin_type,{mfa,0}}}],
+ []} = run_test2(Config, Ts, []),
+ ok.
+
+otp_11771(doc) ->
+ "OTP-11771. Do not allow redefinition of the types arity(_) &c..";
+otp_11771(suite) -> [];
+otp_11771(Config) when is_list(Config) ->
+ Ts = <<"
+ -module(newly).
+
+ -compile(export_all).
+
+ %% No longer allowed in 17.0:
+ -type arity() :: atom().
+ -type bitstring() :: list().
+ -type iodata() :: integer().
+ -type boolean() :: iodata().
+
+ -type t() :: arity() | bitstring() | iodata() | boolean().
+
+ -spec t() -> t().
+
+ t() ->
+ 1.
+ ">>,
+ {errors,[{7,erl_lint,{builtin_type,{arity,0}}},
+ {8,erl_lint,{builtin_type,{bitstring,0}}},
+ {9,erl_lint,{builtin_type,{iodata,0}}},
+ {10,erl_lint,{builtin_type,{boolean,0}}}],
+ []} = run_test2(Config, Ts, []),
+ ok.
+
+otp_11872(doc) ->
+ "OTP-11872. The type map() undefined when exported.";
+otp_11872(suite) -> [];
+otp_11872(Config) when is_list(Config) ->
+ Ts = <<"
+ -module(map).
+
+ -compile(export_all).
+
+ -export_type([map/0, product/0]).
+
+ -opaque map() :: dict().
+
+ -spec t() -> map().
+
+ t() ->
+ 1.
+ ">>,
+ {error,[{6,erl_lint,{undefined_type,{product,0}}}],
+ [{8,erl_lint,{new_var_arity_type,map}}]} =
+ run_test2(Config, Ts, []),
+ ok.
+
export_all(doc) ->
"OTP-7392. Warning for export_all.";
export_all(Config) when is_list(Config) ->
@@ -3243,20 +3330,148 @@ bin_syntax_errors(Config) ->
ok.
predef(doc) ->
- "Predefined types: array(), digraph(), and so on";
+ "OTP-10342: Predefined types: array(), digraph(), and so on";
predef(suite) -> [];
predef(Config) when is_list(Config) ->
W = get_compilation_warnings(Config, "predef", []),
[] = W,
W2 = get_compilation_warnings(Config, "predef2", []),
- [{7,erl_lint,{deprecated_type,{array,0},{array,array},"OTP 18.0"}},
- {12,erl_lint,{deprecated_type,{dict,0},{dict,dict},"OTP 18.0"}},
- {17,erl_lint,{deprecated_type,{digraph,0},{digraph,graph},"OTP 18.0"}},
- {27,erl_lint,{deprecated_type,{gb_set,0},{gb_sets,set},"OTP 18.0"}},
- {32,erl_lint,{deprecated_type,{gb_tree,0},{gb_trees,tree},"OTP 18.0"}},
- {37,erl_lint,{deprecated_type,{queue,0},{queue,queue},"OTP 18.0"}},
- {42,erl_lint,{deprecated_type,{set,0},{sets,set},"OTP 18.0"}},
- {47,erl_lint,{deprecated_type,{tid,0},{ets,tid},"OTP 18.0"}}] = W2,
+ Tag = deprecated_builtin_type,
+ [{7,erl_lint,{Tag,{array,0},{array,array,1},"OTP 18.0"}},
+ {12,erl_lint,{Tag,{dict,0},{dict,dict,2},"OTP 18.0"}},
+ {17,erl_lint,{Tag,{digraph,0},{digraph,graph},"OTP 18.0"}},
+ {27,erl_lint,{Tag,{gb_set,0},{gb_sets,set,1},"OTP 18.0"}},
+ {32,erl_lint,{Tag,{gb_tree,0},{gb_trees,tree,2},"OTP 18.0"}},
+ {37,erl_lint,{Tag,{queue,0},{queue,queue,1},"OTP 18.0"}},
+ {42,erl_lint,{Tag,{set,0},{sets,set,1},"OTP 18.0"}},
+ {47,erl_lint,{Tag,{tid,0},{ets,tid},"OTP 18.0"}}] = W2,
+ Ts = [{otp_10342_1,
+ <<"-compile(nowarn_deprecated_type).
+
+ -spec t(dict()) -> non_neg_integer().
+
+ t(D) ->
+ erlang:phash2(D, 3000).
+ ">>,
+ {[nowarn_unused_function]},
+ []},
+ {otp_10342_2,
+ <<"-spec t(dict()) -> non_neg_integer().
+
+ t(D) ->
+ erlang:phash2(D, 3000).
+ ">>,
+ {[nowarn_unused_function]},
+ {warnings,[{1,erl_lint,
+ {deprecated_builtin_type,{dict,0},{dict,dict,2},
+ "OTP 18.0"}}]}}],
+ [] = run(Config, Ts),
+ ok.
+
+maps(Config) ->
+ %% TODO: test key patterns, not done because map patterns are going to be
+ %% changed a lot.
+ Ts = [{illegal_map_construction,
+ <<"t() ->
+ #{ a := b,
+ c => d,
+ e := f
+ }#{ a := b,
+ c => d,
+ e := f };
+ t() when is_map(#{ a := b,
+ c => d
+ }#{ a := b,
+ c => d,
+ e := f }) ->
+ ok.
+ ">>,
+ [],
+ {errors,[{2,erl_lint,illegal_map_construction},
+ {4,erl_lint,illegal_map_construction},
+ {8,erl_lint,illegal_map_construction}],
+ []}},
+ {illegal_pattern,
+ <<"t(#{ a := A,
+ c => d,
+ e := F,
+ g := 1 + 1,
+ h := _,
+ i := (_X = _Y),
+ j := (x ! y) }) ->
+ {A,F}.
+ ">>,
+ [],
+ {errors,[{2,erl_lint,illegal_pattern},
+ {7,erl_lint,illegal_pattern}],
+ []}},
+ {error_in_illegal_map_construction,
+ <<"t() -> #{ a := X }.">>,
+ [],
+ {errors,[{1,erl_lint,illegal_map_construction},
+ {1,erl_lint,{unbound_var,'X'}}],
+ []}},
+ {errors_in_map_keys,
+ <<"t(V) -> #{ a => 1,
+ #{a=>V} => 2,
+ #{ \"hi\" => wazzup, hi => ho } => yep,
+ [try a catch _:_ -> b end] => nope,
+ ok => 1.0,
+ [3+3] => nope,
+ 1.0 => yep,
+ {3.0+3} => nope,
+ {yep} => yep,
+ [case a of a -> a end] => nope
+ }.
+ ">>,
+ [],
+ {errors,[{2,erl_lint,{illegal_map_key_variable,'V'}},
+ {4,erl_lint,illegal_map_key},
+ {6,erl_lint,illegal_map_key},
+ {8,erl_lint,illegal_map_key},
+ {10,erl_lint,illegal_map_key}],[]}},
+ {errors_in_map_keys_pattern,
+ <<"t(#{ a := 2,
+ #{} := A,
+ #{ 3 => 33 } := hi,
+ #{ 3 := 33 } := hi,
+ #{ hi => 54, \"hello\" => 45 } := hi,
+ #{ V => 33 } := hi }) ->
+ A.
+ ">>,
+ [],
+ {errors,[{4,erl_lint,illegal_map_key},
+ {6,erl_lint,{illegal_map_key_variable,'V'}}],[]}}],
+ [] = run(Config, Ts),
+ ok.
+
+maps_type(Config) when is_list(Config) ->
+ Ts = [
+ {maps_type1,
+ <<"
+ -type m() :: #{a => integer()}.
+ -spec t1(#{k=>term()}) -> {term(), map()}.
+
+ t1(#{k:=V}=M) -> {V,M}.
+
+ -spec t2(m()) -> integer().
+
+ t2(#{a:=V}) -> V.
+ ">>,
+ [],
+ []},
+ {maps_type2,
+ <<"
+ %% Built-in var arity map type:
+ -type map() :: tuple().
+ -type a() :: map().
+
+ -spec t(a()) -> a().
+ t(M) -> M.
+ ">>,
+ [],
+ {warnings,[{3,erl_lint,{new_var_arity_type,map}}]}}],
+ [] = run(Config, Ts),
ok.
run(Config, Tests) ->
diff --git a/lib/stdlib/test/erl_lint_SUITE_data/predef.erl b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl
index c2364fd1c2..ee9073aa67 100644
--- a/lib/stdlib/test/erl_lint_SUITE_data/predef.erl
+++ b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl
@@ -5,8 +5,8 @@
-export_type([array/0, digraph/0, gb_set/0]).
-%% Before R17B local re-definitions of pre-defined opaque types were
-%% ignored but did not generate any warning.
+%% Before Erlang/OTP 17.0 local re-definitions of pre-defined opaque
+%% types were ignored but did not generate any warning.
-opaque array() :: atom().
-opaque digraph() :: atom().
-opaque gb_set() :: atom().
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 390322a5fa..babf3a49eb 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -993,6 +993,16 @@ maps_syntax(Config) when is_list(Config) ->
ok = pp_expr(<<"#{ a => 1, <<\"hi\">> => \"world\", 33 => 1.0 }">>),
ok = pp_expr(<<"#{ a := V1, <<\"hi\">> := V2 } = M">>),
ok = pp_expr(<<"M#{ a => V1, <<\"hi\">> := V2 }">>),
+ F = <<"-module(maps_type_syntax).\n"
+ "-compile(export_all).\n"
+ "-type t1() :: map().\n"
+ "-type t2() :: #{ atom() => integer(), atom() => float() }.\n"
+ "-spec f1(t1()) -> 'true'.\n"
+ "f1(M) when is_map(M) -> true.\n"
+ "-spec f2(t2()) -> integer().\n"
+ "f2(#{a := V1,b := V2}) -> V1 + V2.\n"
+ "\n">>,
+ ok = pp_forms(F),
ok.
diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl
index 447e159cd4..35067e8116 100644
--- a/lib/stdlib/test/erl_scan_SUITE.erl
+++ b/lib/stdlib/test/erl_scan_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,7 @@
init_per_group/2,end_per_group/2]).
-export([ error_1/1, error_2/1, iso88591/1, otp_7810/1, otp_10302/1,
- otp_10990/1, otp_10992/1]).
+ otp_10990/1, otp_10992/1, otp_11807/1]).
-import(lists, [nth/2,flatten/1]).
-import(io_lib, [print/1]).
@@ -60,7 +60,8 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [{group, error}, iso88591, otp_7810, otp_10302, otp_10990, otp_10992].
+ [{group, error}, iso88591, otp_7810, otp_10302, otp_10990, otp_10992,
+ otp_11807].
groups() ->
[{error, [], [error_1, error_2]}].
@@ -1144,6 +1145,25 @@ otp_10992(Config) when is_list(Config) ->
erl_parse:abstract([$A,42.0], [{encoding,utf8}]),
ok.
+otp_11807(doc) ->
+ "OTP-11807. Generalize erl_parse:abstract/2.";
+otp_11807(suite) ->
+ [];
+otp_11807(Config) when is_list(Config) ->
+ {cons,0,{integer,0,97},{cons,0,{integer,0,98},{nil,0}}} =
+ erl_parse:abstract("ab", [{encoding,none}]),
+ {cons,0,{integer,0,-1},{nil,0}} =
+ erl_parse:abstract([-1], [{encoding,latin1}]),
+ ASCII = fun(I) -> I >= 0 andalso I < 128 end,
+ {string,0,"xyz"} = erl_parse:abstract("xyz", [{encoding,ASCII}]),
+ {cons,0,{integer,0,228},{nil,0}} =
+ erl_parse:abstract([228], [{encoding,ASCII}]),
+ {cons,0,{integer,0,97},{atom,0,a}} =
+ erl_parse:abstract("a"++a, [{encoding,latin1}]),
+ {'EXIT', {{badarg,bad},_}} = % minor backward incompatibility
+ (catch erl_parse:abstract("string", [{encoding,bad}])),
+ ok.
+
test_string(String, Expected) ->
{ok, Expected, _End} = erl_scan:string(String),
test(String).
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index 4a67d68428..8203a03a7a 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -23,7 +23,7 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
wildcard_one/1,wildcard_two/1,wildcard_errors/1,
- fold_files/1,otp_5960/1,ensure_dir_eexist/1]).
+ fold_files/1,otp_5960/1,ensure_dir_eexist/1,symlinks/1]).
-import(lists, [foreach/2]).
@@ -43,7 +43,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[wildcard_one, wildcard_two, wildcard_errors,
- fold_files, otp_5960, ensure_dir_eexist].
+ fold_files, otp_5960, ensure_dir_eexist, symlinks].
groups() ->
[].
@@ -366,3 +366,39 @@ ensure_dir_eexist(Config) when is_list(Config) ->
?line {error, eexist} = filelib:ensure_dir(NeedFile),
?line {error, eexist} = filelib:ensure_dir(NeedFileB),
ok.
+
+symlinks(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ Dir = filename:join(PrivDir, ?MODULE_STRING++"_symlinks"),
+ SubDir = filename:join(Dir, "sub"),
+ AFile = filename:join(SubDir, "a_file"),
+ Alias = filename:join(Dir, "symlink"),
+ ok = file:make_dir(Dir),
+ ok = file:make_dir(SubDir),
+ ok = file:write_file(AFile, "not that big\n"),
+ case file:make_symlink(AFile, Alias) of
+ {error, enotsup} ->
+ {skip, "Links not supported on this platform"};
+ {error, eperm} ->
+ {win32,_} = os:type(),
+ {skip, "Windows user not privileged to create symlinks"};
+ ok ->
+ ["sub","symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "*"))),
+ ["symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"))),
+ ok = file:delete(AFile),
+ %% The symlink should still be visible even when its target
+ %% has been deleted.
+ ["sub","symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "*"))),
+ ["symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"))),
+ ok
+ end.
+
+basenames(Dir, Files) ->
+ [begin
+ Dir = filename:dirname(F),
+ filename:basename(F)
+ end || F <- Files].
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 5819ef3890..60a1ba8c60 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -974,6 +974,10 @@ get_state(Config) when is_list(Config) ->
[{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result1),
Result2 = sys:get_state(Pid, 5000),
[{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result2),
+ ok = sys:suspend(Pid),
+ Result3 = sys:get_state(Pid),
+ [{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result3),
+ ok = sys:resume(Pid),
ok = gen_event:stop(Pid),
ok.
@@ -998,4 +1002,11 @@ replace_state(Config) when is_list(Config) ->
Replace3 = fun(_) -> exit(fail) end,
[{dummy1_h,false,NState2}] = sys:replace_state(Pid, Replace3),
[{dummy1_h,false,NState2}] = sys:get_state(Pid),
+ %% verify state replaced if process sys suspended
+ NState3 = "replaced again and again",
+ Replace4 = fun({dummy1_h,false,_}=S) -> setelement(3,S,NState3) end,
+ ok = sys:suspend(Pid),
+ [{dummy1_h,false,NState3}] = sys:replace_state(Pid, Replace4),
+ ok = sys:resume(Pid),
+ [{dummy1_h,false,NState3}] = sys:get_state(Pid),
ok.
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index fd15838b7d..8aeec07ae8 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -426,6 +426,14 @@ get_state(Config) when is_list(Config) ->
{idle, State} = sys:get_state(gfsm),
{idle, State} = sys:get_state(gfsm, 5000),
stop_it(Pid2),
+
+ %% check that get_state works when pid is sys suspended
+ {ok, Pid3} = gen_fsm:start(gen_fsm_SUITE, {state_data, State}, []),
+ {idle, State} = sys:get_state(Pid3),
+ ok = sys:suspend(Pid3),
+ {idle, State} = sys:get_state(Pid3, 5000),
+ ok = sys:resume(Pid3),
+ stop_it(Pid3),
ok.
replace_state(Config) when is_list(Config) ->
@@ -442,8 +450,18 @@ replace_state(Config) when is_list(Config) ->
{state0, NState2} = sys:get_state(Pid),
%% verify no change in state if replace function crashes
Replace3 = fun(_) -> error(fail) end,
- {state0, NState2} = sys:replace_state(Pid, Replace3),
+ {'EXIT',{{callback_failed,
+ {gen_fsm,system_replace_state},{error,fail}},_}} =
+ (catch sys:replace_state(Pid, Replace3)),
{state0, NState2} = sys:get_state(Pid),
+ %% verify state replaced if process sys suspended
+ ok = sys:suspend(Pid),
+ Suffix2 = " and again",
+ NState3 = NState2 ++ Suffix2,
+ Replace4 = fun({StateName, _}) -> {StateName, NState3} end,
+ {state0, NState3} = sys:replace_state(Pid, Replace4),
+ ok = sys:resume(Pid),
+ {state0, NState3} = sys:get_state(Pid, 5000),
stop_it(Pid),
ok.
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index a360a0809b..960e7f60e7 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -1049,6 +1049,9 @@ get_state(Config) when is_list(Config) ->
{ok, Pid} = gen_server:start_link(?MODULE, {state,State}, []),
State = sys:get_state(Pid),
State = sys:get_state(Pid, 5000),
+ ok = sys:suspend(Pid),
+ State = sys:get_state(Pid),
+ ok = sys:resume(Pid),
ok.
%% Verify that sys:replace_state correctly replaces gen_server state
@@ -1075,8 +1078,18 @@ replace_state(Config) when is_list(Config) ->
NState2 = sys:get_state(Pid, 5000),
%% verify no change in state if replace function crashes
Replace3 = fun(_) -> throw(fail) end,
- NState2 = sys:replace_state(Pid, Replace3),
+ {'EXIT',{{callback_failed,
+ {gen_server,system_replace_state},{throw,fail}},_}} =
+ (catch sys:replace_state(Pid, Replace3)),
NState2 = sys:get_state(Pid, 5000),
+ %% verify state replaced if process sys suspended
+ ok = sys:suspend(Pid),
+ Suffix2 = " and again",
+ NState3 = NState2 ++ Suffix2,
+ Replace4 = fun(S) -> S ++ Suffix2 end,
+ NState3 = sys:replace_state(Pid, Replace4),
+ ok = sys:resume(Pid),
+ NState3 = sys:get_state(Pid, 5000),
ok.
%% Test that the time for a huge message queue is not
diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl
new file mode 100644
index 0000000000..c826ee731a
--- /dev/null
+++ b/lib/stdlib/test/maps_SUITE.erl
@@ -0,0 +1,69 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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%
+%%
+%%%----------------------------------------------------------------
+%%% Purpose: Test suite for the 'maps' module.
+%%%-----------------------------------------------------------------
+
+-module(maps_SUITE).
+
+-include_lib("test_server/include/test_server.hrl").
+
+% Default timetrap timeout (set in init_per_testcase).
+% This should be set relatively high (10-15 times the expected
+% max testcasetime).
+-define(default_timeout, ?t:minutes(4)).
+
+% Test server specific exports
+-export([all/0]).
+-export([suite/0]).
+-export([init_per_suite/1]).
+-export([end_per_suite/1]).
+-export([init_per_testcase/2]).
+-export([end_per_testcase/2]).
+
+-export([get3/1]).
+
+suite() ->
+ [{ct_hooks, [ts_install_cth]}].
+
+all() ->
+ [get3].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_Case, Config) ->
+ ?line Dog=test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+get3(Config) when is_list(Config) ->
+ Map = #{ key1 => value1, key2 => value2 },
+ DefaultValue = "Default value",
+ ?line value1 = maps:get(key1, Map, DefaultValue),
+ ?line value2 = maps:get(key2, Map, DefaultValue),
+ ?line DefaultValue = maps:get(key3, Map, DefaultValue),
+ ok.
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 2846657c09..37fbb5267b 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -72,7 +72,7 @@
otp_5644/1, otp_5195/1, otp_6038_bug/1, otp_6359/1, otp_6562/1,
otp_6590/1, otp_6673/1, otp_6964/1, otp_7114/1, otp_7238/1,
- otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1,
+ otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1, otp_11758/1,
manpage/1,
@@ -142,7 +142,7 @@ groups() ->
{tickets, [],
[otp_5644, otp_5195, otp_6038_bug, otp_6359, otp_6562,
otp_6590, otp_6673, otp_6964, otp_7114, otp_7232,
- otp_7238, otp_7552, otp_6674, otp_7714]},
+ otp_7238, otp_7552, otp_6674, otp_7714, otp_11758]},
{compat, [], [backward, forward]}].
init_per_suite(Config) ->
@@ -6670,6 +6670,19 @@ otp_7714(Config) when is_list(Config) ->
ets:delete(E2)">>],
?line run(Config, Ts).
+otp_11758(doc) ->
+ "OTP-11758. Bug.";
+otp_11758(suite) -> [];
+otp_11758(Config) when is_list(Config) ->
+ Ts = [<<"T = ets:new(r, [{keypos, 2}]),
+ L = [{rrr, xxx, aaa}, {rrr, yyy, bbb}],
+ true = ets:insert(T, L),
+ QH = qlc:q([{rrr, B, C} || {rrr, B, C} <- ets:table(T),
+ (B =:= xxx) or (B =:= yyy) and (C =:= aaa)]),
+ [{rrr,xxx,aaa}] = qlc:e(QH),
+ ets:delete(T)">>],
+ run(Config, Ts).
+
otp_6674(doc) ->
"OTP-6674. match/comparison.";
otp_6674(suite) -> [];
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index 692dfe0faa..e016432f4d 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -54,7 +54,7 @@ config(priv_dir,_) ->
-include_lib("test_server/include/test_server.hrl").
-export([init_per_testcase/2, end_per_testcase/2]).
% Default timetrap timeout (set in init_per_testcase).
--define(default_timeout, ?t:minutes(2)).
+-define(default_timeout, ?t:minutes(10)).
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
?line OrigPath = code:get_path(),
diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl
index 53a34511d9..59821220b4 100644
--- a/lib/stdlib/test/stdlib_SUITE.erl
+++ b/lib/stdlib/test/stdlib_SUITE.erl
@@ -94,10 +94,10 @@ appup_tests(App,{OkVsns,NokVsns}) ->
ok.
create_test_vsns(App) ->
- This = erlang:system_info(otp_release),
- FirstMajor = previous_major(This),
+ ThisMajor = erlang:system_info(otp_release),
+ FirstMajor = previous_major(ThisMajor),
SecondMajor = previous_major(FirstMajor),
- Ok = app_vsn(App,[FirstMajor]),
+ Ok = app_vsn(App,[ThisMajor,FirstMajor]),
Nok0 = app_vsn(App,[SecondMajor]),
Nok = case Ok of
[Ok1|_] ->
@@ -108,9 +108,9 @@ create_test_vsns(App) ->
{Ok,Nok}.
previous_major("17") ->
- "r16";
-previous_major("r"++Rel) ->
- "r"++previous_major(Rel);
+ "r16b";
+previous_major("r16b") ->
+ "r15b";
previous_major(Rel) ->
integer_to_list(list_to_integer(Rel)-1).
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index ac5a34c3bc..836ea7c030 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -60,7 +60,7 @@
simple_one_for_one_extra/1, simple_one_for_one_shutdown/1]).
%% Misc tests
--export([child_unlink/1, tree/1, count_children_memory/1,
+-export([child_unlink/1, tree/1, count_children/1,
do_not_save_start_parameters_for_temporary_children/1,
do_not_save_child_specs_for_temporary_children/1,
simple_one_for_one_scale_many_temporary_children/1,
@@ -82,7 +82,7 @@ all() ->
{group, normal_termination},
{group, shutdown_termination},
{group, abnormal_termination}, child_unlink, tree,
- count_children_memory, do_not_save_start_parameters_for_temporary_children,
+ count_children, do_not_save_start_parameters_for_temporary_children,
do_not_save_child_specs_for_temporary_children,
simple_one_for_one_scale_many_temporary_children, temporary_bystander,
simple_global_supervisor, hanging_restart_loop, hanging_restart_loop_simple].
@@ -129,23 +129,10 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-init_per_testcase(count_children_memory, Config) ->
- try erlang:memory() of
- _ ->
- erts_debug:set_internal_state(available_internal_state, true),
- Dog = ?t:timetrap(?TIMEOUT),
- [{watchdog,Dog}|Config]
- catch error:notsup ->
- {skip, "+Meamin used during test; erlang:memory/1 not available"}
- end;
init_per_testcase(_Case, Config) ->
Dog = ?t:timetrap(?TIMEOUT),
[{watchdog,Dog}|Config].
-end_per_testcase(count_children_memory, Config) ->
- catch erts_debug:set_internal_state(available_internal_state, false),
- ?t:timetrap_cancel(?config(watchdog,Config)),
- ok;
end_per_testcase(_Case, Config) ->
?t:timetrap_cancel(?config(watchdog,Config)),
ok.
@@ -1249,34 +1236,24 @@ tree(Config) when is_list(Config) ->
[0,0,0,0] = get_child_counts(NewSup2).
%%-------------------------------------------------------------------------
-%% Test that count_children does not eat memory.
-count_children_memory(Config) when is_list(Config) ->
+%% Test count_children
+count_children(Config) when is_list(Config) ->
process_flag(trap_exit, true),
Child = {child, {supervisor_1, start_child, []}, temporary, 1000,
worker, []},
{ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}),
[supervisor:start_child(sup_test, []) || _Ignore <- lists:seq(1,1000)],
- garbage_collect(),
- _Size1 = proc_memory(),
Children = supervisor:which_children(sup_test),
- _Size2 = proc_memory(),
ChildCount = get_child_counts(sup_test),
- _Size3 = proc_memory(),
[supervisor:start_child(sup_test, []) || _Ignore2 <- lists:seq(1,1000)],
- garbage_collect(),
- Children2 = supervisor:which_children(sup_test),
- Size4 = proc_memory(),
ChildCount2 = get_child_counts(sup_test),
- Size5 = proc_memory(),
+ Children2 = supervisor:which_children(sup_test),
- garbage_collect(),
- Children3 = supervisor:which_children(sup_test),
- Size6 = proc_memory(),
ChildCount3 = get_child_counts(sup_test),
- Size7 = proc_memory(),
+ Children3 = supervisor:which_children(sup_test),
1000 = length(Children),
[1,1000,0,1000] = ChildCount,
@@ -1285,27 +1262,9 @@ count_children_memory(Config) when is_list(Config) ->
Children3 = Children2,
ChildCount3 = ChildCount2,
- %% count_children consumes memory using an accumulator function,
- %% but the space can be reclaimed incrementally,
- %% which_children may generate garbage that will be reclaimed later.
- case (Size5 =< Size4) of
- true -> ok;
- false ->
- test_server:fail({count_children, used_more_memory,Size4,Size5})
- end,
- case Size7 =< Size6 of
- true -> ok;
- false ->
- test_server:fail({count_children, used_more_memory,Size6,Size7})
- end,
-
[terminate(SupPid, Pid, child, kill) || {undefined, Pid, worker, _Modules} <- Children3],
[1,0,0,0] = get_child_counts(sup_test).
-proc_memory() ->
- erts_debug:set_internal_state(wait, deallocations),
- erlang:memory(processes_used).
-
%%-------------------------------------------------------------------------
%% Temporary children shall not be restarted so they should not save
%% start parameters, as it potentially can take up a huge amount of
@@ -1483,7 +1442,7 @@ simple_one_for_one_scale_many_temporary_children(_Config) ->
if T1 > 0 ->
Scaling = T2 div T1,
- if Scaling > 20 ->
+ if Scaling > 50 ->
%% The scaling shoul be linear (i.e.10, really), but we
%% give some extra here to avoid failing the test
%% unecessarily.
diff --git a/lib/stdlib/test/sys_SUITE.erl b/lib/stdlib/test/sys_SUITE.erl
index c06ba545e7..f38bc87ae5 100644
--- a/lib/stdlib/test/sys_SUITE.erl
+++ b/lib/stdlib/test/sys_SUITE.erl
@@ -19,7 +19,7 @@
-module(sys_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,log/1,log_to_file/1,
- stats/1,trace/1,suspend/1,install/1]).
+ stats/1,trace/1,suspend/1,install/1,special_process/1]).
-export([handle_call/3,terminate/2,init/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -27,14 +27,12 @@
%% Doesn't look into change_code at all
-%% Doesn't address writing your own process that understands
-%% system messages at all.
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [log, log_to_file, stats, trace, suspend, install].
+ [log, log_to_file, stats, trace, suspend, install, special_process].
groups() ->
[].
@@ -157,6 +155,84 @@ install(Config) when is_list(Config) ->
?line stop(),
ok.
+special_process(suite) -> [];
+special_process(Config) when is_list(Config) ->
+ ok = spec_proc(sys_sp1),
+ ok = spec_proc(sys_sp2).
+
+spec_proc(Mod) ->
+ {ok,_} = Mod:start_link(100),
+ ok = sys:statistics(Mod,true),
+ ok = sys:trace(Mod,true),
+ 1 = Ch = Mod:alloc(),
+ Free = lists:seq(2,100),
+ Replace = case sys:get_state(Mod) of
+ {[Ch],Free} ->
+ fun({A,F}) ->
+ Free = F,
+ {A,[2,3,4]}
+ end;
+ {state,[Ch],Free} ->
+ fun({state,A,F}) ->
+ Free = F,
+ {state,A,[2,3,4]}
+ end
+ end,
+ case sys:replace_state(Mod, Replace) of
+ {[Ch],[2,3,4]} -> ok;
+ {state,[Ch],[2,3,4]} -> ok
+ end,
+ ok = Mod:free(Ch),
+ case sys:get_state(Mod) of
+ {[],[1,2,3,4]} -> ok;
+ {state,[],[1,2,3,4]} -> ok
+ end,
+ {ok,[{start_time,_},
+ {current_time,_},
+ {reductions,_},
+ {messages_in,2},
+ {messages_out,1}]} = sys:statistics(Mod,get),
+ ok = sys:statistics(Mod,false),
+ [] = sys:replace_state(Mod, fun(_) -> [] end),
+ process_flag(trap_exit,true),
+ ok = case catch sys:get_state(Mod) of
+ [] ->
+ ok;
+ {'EXIT',{{callback_failed,
+ {Mod,system_get_state},{throw,fail}},_}} ->
+ ok
+ end,
+ Mod:stop(),
+ WaitForUnregister = fun W() ->
+ case whereis(Mod) of
+ undefined -> ok;
+ _ -> timer:sleep(10), W()
+ end
+ end,
+ WaitForUnregister(),
+ {ok,_} = Mod:start_link(4),
+ ok = case catch sys:replace_state(Mod, fun(_) -> {} end) of
+ {} ->
+ ok;
+ {'EXIT',{{callback_failed,
+ {Mod,system_replace_state},{throw,fail}},_}} ->
+ ok
+ end,
+ Mod:stop(),
+ WaitForUnregister(),
+ {ok,_} = Mod:start_link(4),
+ StateFun = fun(_) -> error(fail) end,
+ ok = case catch sys:replace_state(Mod, StateFun) of
+ {} ->
+ ok;
+ {'EXIT',{{callback_failed,
+ {Mod,system_replace_state},{error,fail}},_}} ->
+ ok;
+ {'EXIT',{{callback_failed,StateFun,{error,fail}},_}} ->
+ ok
+ end,
+ Mod:stop().
+
%%%%%%%%%%%%%%%%%%%%
%% Dummy server
diff --git a/lib/stdlib/test/sys_sp1.erl b/lib/stdlib/test/sys_sp1.erl
new file mode 100644
index 0000000000..e84ffcfa12
--- /dev/null
+++ b/lib/stdlib/test/sys_sp1.erl
@@ -0,0 +1,114 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(sys_sp1).
+-export([start_link/1, stop/0]).
+-export([alloc/0, free/1]).
+-export([init/1]).
+-export([system_continue/3, system_terminate/4,
+ write_debug/3,
+ system_get_state/1, system_replace_state/2]).
+
+%% Implements the ch4 example from the Design Principles doc. Same as
+%% sys_sp2 except this module exports system_get_state/1 and
+%% system_replace_state/2
+
+start_link(NumCh) ->
+ proc_lib:start_link(?MODULE, init, [[self(),NumCh]]).
+
+stop() ->
+ ?MODULE ! stop,
+ ok.
+
+alloc() ->
+ ?MODULE ! {self(), alloc},
+ receive
+ {?MODULE, Res} ->
+ Res
+ end.
+
+free(Ch) ->
+ ?MODULE ! {free, Ch},
+ ok.
+
+init([Parent,NumCh]) ->
+ register(?MODULE, self()),
+ Chs = channels(NumCh),
+ Deb = sys:debug_options([]),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ loop(Chs, Parent, Deb).
+
+loop(Chs, Parent, Deb) ->
+ receive
+ {From, alloc} ->
+ Deb2 = sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, alloc, From}),
+ {Ch, Chs2} = alloc(Chs),
+ From ! {?MODULE, Ch},
+ Deb3 = sys:handle_debug(Deb2, fun write_debug/3,
+ ?MODULE, {out, {?MODULE, Ch}, From}),
+ loop(Chs2, Parent, Deb3);
+ {free, Ch} ->
+ Deb2 = sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, {free, Ch}}),
+ Chs2 = free(Ch, Chs),
+ loop(Chs2, Parent, Deb2);
+ {system, From, Request} ->
+ sys:handle_system_msg(Request, From, Parent,
+ ?MODULE, Deb, Chs);
+ stop ->
+ sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, stop}),
+ ok
+ end.
+
+system_continue(Parent, Deb, Chs) ->
+ loop(Chs, Parent, Deb).
+
+system_terminate(Reason, _Parent, _Deb, _Chs) ->
+ exit(Reason).
+
+system_get_state([]) ->
+ throw(fail);
+system_get_state(Chs) ->
+ {ok, Chs}.
+
+system_replace_state(_StateFun, {}) ->
+ throw(fail);
+system_replace_state(StateFun, Chs) ->
+ NChs = StateFun(Chs),
+ {ok, NChs, NChs}.
+
+write_debug(Dev, Event, Name) ->
+ io:format(Dev, "~p event = ~p~n", [Name, Event]).
+
+channels(NumCh) ->
+ {_Allocated=[], _Free=lists:seq(1,NumCh)}.
+
+alloc({_, []}) ->
+ {error, "no channels available"};
+alloc({Allocated, [H|T]}) ->
+ {H, {[H|Allocated], T}}.
+
+free(Ch, {Alloc, Free}=Channels) ->
+ case lists:member(Ch, Alloc) of
+ true ->
+ {lists:delete(Ch, Alloc), [Ch|Free]};
+ false ->
+ Channels
+ end.
diff --git a/lib/stdlib/test/sys_sp2.erl b/lib/stdlib/test/sys_sp2.erl
new file mode 100644
index 0000000000..56a5e4d071
--- /dev/null
+++ b/lib/stdlib/test/sys_sp2.erl
@@ -0,0 +1,107 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(sys_sp2).
+-export([start_link/1, stop/0]).
+-export([alloc/0, free/1]).
+-export([init/1]).
+-export([system_continue/3, system_terminate/4,
+ write_debug/3]).
+
+%% Implements the ch4 example from the Design Principles doc. Same as
+%% sys_sp1 except this module does not export system_get_state/1 or
+%% system_replace_state/2
+
+start_link(NumCh) ->
+ proc_lib:start_link(?MODULE, init, [[self(),NumCh]]).
+
+stop() ->
+ ?MODULE ! stop,
+ ok.
+
+alloc() ->
+ ?MODULE ! {self(), alloc},
+ receive
+ {?MODULE, Res} ->
+ Res
+ end.
+
+free(Ch) ->
+ ?MODULE ! {free, Ch},
+ ok.
+
+%% can't use 2-tuple for state here as we do in sys_sp1, since the 2-tuple
+%% is not compatible with the backward compatibility handling for
+%% sys:get_state in sys.erl
+-record(state, {alloc,free}).
+
+init([Parent,NumCh]) ->
+ register(?MODULE, self()),
+ Chs = channels(NumCh),
+ Deb = sys:debug_options([]),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ loop(Chs, Parent, Deb).
+
+loop(Chs, Parent, Deb) ->
+ receive
+ {From, alloc} ->
+ Deb2 = sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, alloc, From}),
+ {Ch, Chs2} = alloc(Chs),
+ From ! {?MODULE, Ch},
+ Deb3 = sys:handle_debug(Deb2, fun write_debug/3,
+ ?MODULE, {out, {?MODULE, Ch}, From}),
+ loop(Chs2, Parent, Deb3);
+ {free, Ch} ->
+ Deb2 = sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, {free, Ch}}),
+ Chs2 = free(Ch, Chs),
+ loop(Chs2, Parent, Deb2);
+ {system, From, Request} ->
+ sys:handle_system_msg(Request, From, Parent,
+ ?MODULE, Deb, Chs);
+ stop ->
+ sys:handle_debug(Deb, fun write_debug/3,
+ ?MODULE, {in, stop}),
+ ok
+ end.
+
+system_continue(Parent, Deb, Chs) ->
+ loop(Chs, Parent, Deb).
+
+system_terminate(Reason, _Parent, _Deb, _Chs) ->
+ exit(Reason).
+
+write_debug(Dev, Event, Name) ->
+ io:format(Dev, "~p event = ~p~n", [Name, Event]).
+
+channels(NumCh) ->
+ #state{alloc=[], free=lists:seq(1,NumCh)}.
+
+alloc(#state{free=[]}=Channels) ->
+ {{error, "no channels available"}, Channels};
+alloc(#state{alloc=Allocated, free=[H|T]}) ->
+ {H, #state{alloc=[H|Allocated], free=T}}.
+
+free(Ch, #state{alloc=Alloc, free=Free}=Channels) ->
+ case lists:member(Ch, Alloc) of
+ true ->
+ #state{alloc=lists:delete(Ch, Alloc), free=[Ch|Free]};
+ false ->
+ Channels
+ end.
diff --git a/lib/stdlib/test/tar_SUITE.erl b/lib/stdlib/test/tar_SUITE.erl
index 5bc34e35af..6349139925 100644
--- a/lib/stdlib/test/tar_SUITE.erl
+++ b/lib/stdlib/test/tar_SUITE.erl
@@ -23,7 +23,7 @@
create_long_names/1, bad_tar/1, errors/1, extract_from_binary/1,
extract_from_binary_compressed/1,
extract_from_open_file/1, symlinks/1, open_add_close/1, cooked_compressed/1,
- memory/1]).
+ memory/1,unicode/1]).
-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
@@ -34,7 +34,7 @@ all() ->
[borderline, atomic, long_names, create_long_names,
bad_tar, errors, extract_from_binary,
extract_from_binary_compressed, extract_from_open_file,
- symlinks, open_add_close, cooked_compressed, memory].
+ symlinks, open_add_close, cooked_compressed, memory, unicode].
groups() ->
[].
@@ -73,6 +73,7 @@ borderline(Config) when is_list(Config) ->
?line lists:foreach(fun(Size) -> borderline_test(Size, TempDir) end,
[0, 1, 10, 13, 127, 333, Record-1, Record, Record+1,
+ Block-2*Record-1, Block-2*Record, Block-2*Record+1,
Block-Record-1, Block-Record, Block-Record+1,
Block-1, Block, Block+1,
Block+Record-1, Block+Record, Block+Record+1]),
@@ -726,6 +727,56 @@ memory(Config) when is_list(Config) ->
?line ok = delete_files([Name1,Name2]),
ok.
+%% Test filenames with characters outside the US ASCII range.
+unicode(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ do_unicode(PrivDir),
+ 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;
+ false ->
+ ok
+ end.
+
+has_transparent_naming() ->
+ case os:type() of
+ {unix,darwin} -> false;
+ {unix,_} -> true;
+ _ -> false
+ end.
+
+do_unicode(PrivDir) ->
+ ok = file:set_cwd(PrivDir),
+ ok = file:make_dir("unicöde"),
+
+ Names = unicode_create_files(),
+ Tar = "unicöde.tar",
+ ok = erl_tar:create(Tar, ["unicöde"], []),
+ {ok,Names} = erl_tar:table(Tar, []),
+ _ = [ok = file:delete(Name) || Name <- Names],
+ ok = erl_tar:extract(Tar),
+ _ = [{ok,_} = file:read_file(Name) || Name <- Names],
+ _ = [ok = file:delete(Name) || Name <- Names],
+ ok = file:del_dir("unicöde"),
+ ok.
+
+unicode_create_files() ->
+ FileA = "unicöde/smörgåsbord",
+ ok = file:write_file(FileA, "yum!\n"),
+ [FileA|case file:native_name_encoding() of
+ utf8 ->
+ FileB = "unicöde/Хороший файл!",
+ ok = file:write_file(FileB, "But almost empty.\n"),
+ [FileB];
+ latin1 ->
+ []
+ end].
+
%% Delete the given list of files.
delete_files([]) -> ok;
delete_files([Item|Rest]) ->
@@ -791,3 +842,14 @@ make_temp_dir(Base, I) ->
ok -> Name;
{error,eexist} -> make_temp_dir(Base, I+1)
end.
+
+start_node(Name, Args) ->
+ [_,Host] = string:tokens(atom_to_list(node()), "@"),
+ ct:log("Trying to start ~w@~s~n", [Name,Host]),
+ case test_server:start_node(Name, peer, [{args,Args}]) of
+ {error,Reason} ->
+ test_server:fail(Reason);
+ {ok,Node} ->
+ ct:log("Node ~p started~n", [Node]),
+ Node
+ end.
diff --git a/lib/stdlib/test/unicode_SUITE.erl b/lib/stdlib/test/unicode_SUITE.erl
index e2d789bbe6..10b29d0d28 100644
--- a/lib/stdlib/test/unicode_SUITE.erl
+++ b/lib/stdlib/test/unicode_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -98,7 +98,7 @@ ex_binaries_errors_utf8(Config) when is_list(Config) ->
unicode:characters_to_list(Chomped),
BrokenPart = iolist_to_binary(DeepBrokenPart2)
end || X <- lists:seq(1,OKLen) ]
- end || N <- lists:seq(1,20) ],
+ end || N <- lists:seq(1,21,4) ],
ok.
ex_binaries_errors_utf16_little(Config) when is_list(Config) ->
@@ -124,7 +124,7 @@ ex_binaries_errors_utf16_little(Config) when is_list(Config) ->
unicode:characters_to_list(Chomped,{utf16,little}),
BrokenPart = iolist_to_binary(DeepBrokenPart2)
end || X <- lists:seq(1,OKLen) ]
- end || N <- lists:seq(1,15) ],
+ end || N <- lists:seq(1,16,3) ],
ok.
ex_binaries_errors_utf16_big(Config) when is_list(Config) ->
BrokenPart = << <<X:16/big>> || X <- lists:seq(16#DC00,16#DFFF) >>,
@@ -149,7 +149,7 @@ ex_binaries_errors_utf16_big(Config) when is_list(Config) ->
unicode:characters_to_list(Chomped,{utf16,big}),
BrokenPart = iolist_to_binary(DeepBrokenPart2)
end || X <- lists:seq(1,OKLen) ]
- end || N <- lists:seq(1,15) ],
+ end || N <- lists:seq(1,16,3) ],
ok.
ex_binaries_errors_utf32_big(Config) when is_list(Config) ->
@@ -175,7 +175,7 @@ ex_binaries_errors_utf32_big(Config) when is_list(Config) ->
unicode:characters_to_list(Chomped,{utf32,big}),
BrokenPart = iolist_to_binary(DeepBrokenPart2)
end || X <- lists:seq(1,OKLen) ]
- end || N <- lists:seq(1,15) ],
+ end || N <- lists:seq(1,16,3) ],
ok.
ex_binaries_errors_utf32_little(Config) when is_list(Config) ->
@@ -201,7 +201,7 @@ ex_binaries_errors_utf32_little(Config) when is_list(Config) ->
unicode:characters_to_list(Chomped,{utf32,little}),
BrokenPart = iolist_to_binary(DeepBrokenPart2)
end || X <- lists:seq(1,OKLen) ]
- end || N <- lists:seq(1,15) ],
+ end || N <- lists:seq(1,16,3) ],
ok.
diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml
index a9d3f68d1d..4e1e6d8cb1 100644
--- a/lib/syntax_tools/doc/src/notes.xml
+++ b/lib/syntax_tools/doc/src/notes.xml
@@ -31,6 +31,56 @@
<p>This document describes the changes made to the Syntax_Tools
application.</p>
+<section><title>Syntax_Tools 1.6.14</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add implementation of having erl_tidy print to screen
+ instead of writing to the file provided. (Thanks to Aaron
+ France)</p>
+ <p>
+ Own Id: OTP-11632</p>
+ </item>
+ <item>
+ <p>
+ Support Maps syntax in syntax_tools (Thanks to Anthony
+ Ramine).</p>
+ <p>
+ Own Id: OTP-11663</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 1.6.13</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index c9996c954e..46a5ca48df 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -2071,7 +2071,7 @@ map_field_assoc_value(Node) ->
{map_field_assoc, _, _, Value} ->
Value;
_ ->
- (data(Node))#map_field_assoc.name
+ (data(Node))#map_field_assoc.value
end.
@@ -2129,7 +2129,7 @@ map_field_exact_value(Node) ->
{map_field_exact, _, _, Value} ->
Value;
_ ->
- (data(Node))#map_field_exact.name
+ (data(Node))#map_field_exact.value
end.
diff --git a/lib/syntax_tools/src/syntax_tools.app.src b/lib/syntax_tools/src/syntax_tools.app.src
index dc0b9edd62..83dcb5fe23 100644
--- a/lib/syntax_tools/src/syntax_tools.app.src
+++ b/lib/syntax_tools/src/syntax_tools.app.src
@@ -14,4 +14,5 @@
prettypr]},
{registered,[]},
{applications, [stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index d4c54a72aa..6fb3e5ccfb 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -24,12 +24,12 @@
init_per_group/2,end_per_group/2]).
%% Test cases
--export([app_test/1,appup_test/1,smoke_test/1,revert/1]).
+-export([app_test/1,appup_test/1,smoke_test/1,revert/1,revert_map/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test,appup_test,smoke_test,revert].
+ [app_test,appup_test,smoke_test,revert,revert_map].
groups() ->
[].
@@ -109,6 +109,16 @@ revert_file(File, Path) ->
ok
end.
+%% Testing bug fix for reverting map_field_assoc
+revert_map(Config) ->
+ Dog = ?t:timetrap(?t:minutes(1)),
+ ?line [{map_field_assoc,16,{atom,17,name},{var,18,'Value'}}] =
+ erl_syntax:revert_forms([{tree,map_field_assoc,
+ {attr,16,[],none},
+ {map_field_assoc,
+ {atom,17,name},{var,18,'Value'}}}]),
+ ?line ?t:timetrap_cancel(Dog).
+
p_run(Test, List) ->
N = erlang:system_info(schedulers),
p_run_loop(Test, List, N, [], 0).
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index 26153a55f1..cf396ce636 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 1.6.13
+SYNTAX_TOOLS_VSN = 1.6.14
diff --git a/lib/test_server/doc/src/notes.xml b/lib/test_server/doc/src/notes.xml
index d05626094a..556fe94a2a 100644
--- a/lib/test_server/doc/src/notes.xml
+++ b/lib/test_server/doc/src/notes.xml
@@ -32,6 +32,48 @@
<file>notes.xml</file>
</header>
+<section><title>Test_Server 3.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Calls to erlang:open_port/2 with 'spawn' are updated to
+ handle space in the command path.</p>
+ <p>
+ Own Id: OTP-10842</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Test_Server 3.6.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/test_server/doc/src/test_server_ctrl.xml b/lib/test_server/doc/src/test_server_ctrl.xml
index f4aae724e0..0cda531716 100644
--- a/lib/test_server/doc/src/test_server_ctrl.xml
+++ b/lib/test_server/doc/src/test_server_ctrl.xml
@@ -773,11 +773,14 @@ test_server_ctrl:cross_cover_analyse(Level,[{s1,S1LogDir},{s2,S2LogDir}])
<p><c>What = tests_start, Data = {Name,NumCases}</c><br></br>
<c>What = loginfo, Data = [{topdir,TestRootDir},{rundir,CurrLogDir}]</c><br></br>
<c>What = tests_done, Data = {Ok,Failed,{UserSkipped,AutoSkipped}}</c><br></br>
- <c>What = tc_start, Data = {{Mod,Func},TCLogFile}</c><br></br>
- <c>What = tc_done, Data = {Mod,Func,Result}</c><br></br>
- <c>What = tc_user_skip, Data = {Mod,Func,Comment}</c><br></br>
- <c>What = tc_auto_skip, Data = {Mod,Func,Comment}</c><br></br>
+ <c>What = tc_start, Data = {{Mod,{Func,GroupName}},TCLogFile}</c><br></br>
+ <c>What = tc_done, Data = {Mod,{Func,GroupName},Result}</c><br></br>
+ <c>What = tc_user_skip, Data = {Mod,{Func,GroupName},Comment}</c><br></br>
+ <c>What = tc_auto_skip, Data = {Mod,{Func,GroupName},Comment}</c><br></br>
<c>What = framework_error, Data = {{FWMod,FWFunc},Error}</c></p>
+ <p>Note that for a test case function that doesn't belong to a group,
+ <c>GroupName</c> has value <c>undefined</c>, otherwise the name of the test
+ case group.</p>
</desc>
</func>
<func>
diff --git a/lib/test_server/src/test_server.app.src b/lib/test_server/src/test_server.app.src
index 42e78ed279..5672baa6ef 100644
--- a/lib/test_server/src/test_server.app.src
+++ b/lib/test_server/src/test_server.app.src
@@ -31,5 +31,8 @@
test_server,
test_server_break_process]},
{applications, [kernel,stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["tools-2.6.14","stdlib-2.0","runtime_tools-1.8.14",
+ "observer-2.0","kernel-3.0","inets-5.10",
+ "erts-6.0"]}]}.
diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl
index 82672521f7..70dc7a1441 100644
--- a/lib/test_server/src/test_server.erl
+++ b/lib/test_server/src/test_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -444,7 +444,7 @@ run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
%% If this process (group leader of the test case) terminates before
%% all messages have been replied back to the io server, the io server
%% hangs. Fixed by the 20 milli timeout check here, and by using monitor in
-%% io.erl (livrem OCH hangslen mao :)
+%% io.erl.
%%
%% A test case is known to have failed if it returns {'EXIT', _} tuple,
%% or sends a message {failed, File, Line} to it's group_leader
@@ -673,7 +673,7 @@ handle_tc_exit({testcase_aborted,{user_timetrap_error,_}=Msg,_}, St) ->
spawn_fw_call(Mod, Func, Config, Pid, Msg, unknown, self()),
St;
handle_tc_exit(Reason, #st{status={framework,FwMod,FwFunc},
- config=Config,pid=Pid}=St) ->
+ config=Config,pid=Pid}=St) ->
R = case Reason of
{timetrap_timeout,TVal,_} ->
{timetrap,TVal};
@@ -1208,6 +1208,10 @@ do_init_per_testcase(Mod, Args) ->
"a Config list.\n",[]},
{skip,{failed,{Mod,init_per_testcase,bad_return}}}
catch
+ throw:{Skip,Reason} when Skip =:= skip; Skip =:= skipped ->
+ {skip,Reason};
+ exit:{Skip,Reason} when Skip =:= skip; Skip =:= skipped ->
+ {skip,Reason};
throw:Other ->
set_loc(erlang:get_stacktrace()),
Line = get_loc(),
@@ -1378,6 +1382,10 @@ ts_tc(M, F, A) ->
Result = try
apply(M, F, A)
catch
+ throw:{skip, Reason} -> {skip, Reason};
+ throw:{skipped, Reason} -> {skip, Reason};
+ exit:{skip, Reason} -> {skip, Reason};
+ exit:{skipped, Reason} -> {skip, Reason};
Type:Reason ->
Stk = erlang:get_stacktrace(),
set_loc(Stk),
diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl
index dcf905db24..5fbc47a813 100644
--- a/lib/test_server/src/test_server_ctrl.erl
+++ b/lib/test_server/src/test_server_ctrl.erl
@@ -1407,7 +1407,7 @@ remove_conf([{conf, _Ref, Props, _MF}|Cases], NoConf, Repeats) ->
end;
remove_conf([{make,_Ref,_MF}|Cases], NoConf, Repeats) ->
remove_conf(Cases, NoConf, Repeats);
-remove_conf([{skip_case,{{_M,all},_Cmt}}|Cases], NoConf, Repeats) ->
+remove_conf([{skip_case,{{_M,all},_Cmt},_Mode}|Cases], NoConf, Repeats) ->
remove_conf(Cases, NoConf, Repeats);
remove_conf([{skip_case,{Type,_Ref,_MF,_Cmt}}|Cases],
NoConf, Repeats) when Type==conf;
@@ -1431,7 +1431,7 @@ remove_conf([], NoConf, true) ->
remove_conf([], NoConf, false) ->
lists:reverse(NoConf).
-get_suites([{skip_case,{{Mod,_Func},_Cmt}}|Tests], Mods) when is_atom(Mod) ->
+get_suites([{skip_case,{{Mod,_F},_Cmt},_Mode}|Tests], Mods) when is_atom(Mod) ->
case add_mod(Mod, Mods) of
true -> get_suites(Tests, [Mod|Mods]);
false -> get_suites(Tests, Mods)
@@ -1833,7 +1833,7 @@ html_isolate_modules(List, FwMod) ->
html_isolate_modules(List, sets:new(), FwMod).
html_isolate_modules([], Set, _) -> sets:to_list(Set);
-html_isolate_modules([{skip_case,_}|Cases], Set, FwMod) ->
+html_isolate_modules([{skip_case,{_Case,_Cmt},_Mode}|Cases], Set, FwMod) ->
html_isolate_modules(Cases, Set, FwMod);
html_isolate_modules([{conf,_Ref,Props,{FwMod,_Func}}|Cases], Set, FwMod) ->
Set1 = case proplists:get_value(suite, Props) of
@@ -1937,26 +1937,30 @@ copy_html_file(Src, DestDir) ->
add_init_and_end_per_suite([{make,_,_}=Case|Cases], LastMod, LastRef, FwMod) ->
[Case|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)];
-add_init_and_end_per_suite([{skip_case,{{Mod,all},_}}=Case|Cases], LastMod,
+add_init_and_end_per_suite([{skip_case,{{Mod,all},_},_}=Case|Cases], LastMod,
LastRef, FwMod) when Mod =/= LastMod ->
{PreCases, NextMod, NextRef} =
do_add_end_per_suite_and_skip(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
-add_init_and_end_per_suite([{skip_case,{{Mod,_},_}}=Case|Cases], LastMod,
- LastRef, FwMod) when Mod =/= LastMod ->
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
+add_init_and_end_per_suite([{skip_case,{{Mod,_},_Cmt},_Mode}=Case|Cases],
+ LastMod, LastRef, FwMod) when Mod =/= LastMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
-add_init_and_end_per_suite([{skip_case,{conf,_,{Mod,_},_},_}=Case|Cases], LastMod,
- LastRef, FwMod) when Mod =/= LastMod ->
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
+add_init_and_end_per_suite([{skip_case,{conf,_,{Mod,_},_},_}=Case|Cases],
+ LastMod, LastRef, FwMod) when Mod =/= LastMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
add_init_and_end_per_suite([{skip_case,{conf,_,{Mod,_},_}}=Case|Cases], LastMod,
LastRef, FwMod) when Mod =/= LastMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
add_init_and_end_per_suite([{conf,Ref,Props,{FwMod,Func}}=Case|Cases], LastMod,
LastRef, FwMod) ->
%% if Mod == FwMod, this conf test is (probably) a test case group where
@@ -1977,7 +1981,8 @@ add_init_and_end_per_suite([{conf,_,_,{Mod,_}}=Case|Cases], LastMod,
LastRef, FwMod) when Mod =/= LastMod, Mod =/= FwMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
add_init_and_end_per_suite([SkipCase|Cases], LastMod, LastRef, FwMod)
when element(1,SkipCase) == skip_case ->
[SkipCase|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)];
@@ -1987,12 +1992,14 @@ add_init_and_end_per_suite([{Mod,_}=Case|Cases], LastMod, LastRef, FwMod)
when Mod =/= LastMod, Mod =/= FwMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
add_init_and_end_per_suite([{Mod,_,_}=Case|Cases], LastMod, LastRef, FwMod)
when Mod =/= LastMod, Mod =/= FwMod ->
{PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod),
- PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)];
+ PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod,
+ NextRef, FwMod)];
add_init_and_end_per_suite([Case|Cases], LastMod, LastRef, FwMod)->
[Case|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)];
add_init_and_end_per_suite([], _LastMod, undefined, _FwMod) ->
@@ -2169,7 +2176,7 @@ run_test_cases(TestSpec, Config, TimetrapData) ->
%% comment (which gets printed in the log files) describes why the case
%% was skipped.
%%
-%% {skip_case,{Case,Comment}} A normal test case skipped by the user.
+%% {skip_case,{Case,Comment},Mode} A normal test case skipped by the user.
%% The comment (which gets printed in the log files) describes why the
%% case was skipped.
%%
@@ -2342,7 +2349,7 @@ run_test_cases_loop([{SkipTag,{Type,Ref,Case,Comment},SkipMode}|Cases],
ParentRef ->
Reason = {group_result,GrName,failed},
skip_cases_upto(ParentRef, Cases,
- Reason, tc, Mode,
+ Reason, tc, ParentMode,
SkipTag)
end;
false ->
@@ -2401,22 +2408,27 @@ run_test_cases_loop([{auto_skip_case,{Case,Comment},SkipMode}|Cases],
Config, TimetrapData, Mode, Status) ->
{Mod,Func} = skip_case(auto, undefined, get(test_server_case_num)+1,
Case, Comment, is_io_buffered(), SkipMode),
- test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]),
+ test_server_sup:framework_call(report, [tc_auto_skip,
+ {Mod,{Func,get_name(SkipMode)},
+ Comment}]),
run_test_cases_loop(Cases, Config, TimetrapData, Mode,
update_status(skipped, Mod, Func, Status));
-run_test_cases_loop([{skip_case,{{Mod,all}=Case,Comment}}|Cases],
+run_test_cases_loop([{skip_case,{{Mod,all}=Case,Comment},SkipMode}|Cases],
Config, TimetrapData, Mode, Status) ->
- skip_case(user, undefined, 0, Case, Comment, false, Mode),
+ skip_case(user, undefined, 0, Case, Comment, false, SkipMode),
test_server_sup:framework_call(report, [tc_user_skip,
- {Mod,all,Comment}]),
+ {Mod,{all,get_name(SkipMode)},
+ Comment}]),
run_test_cases_loop(Cases, Config, TimetrapData, Mode, Status);
-run_test_cases_loop([{skip_case,{Case,Comment}}|Cases],
+run_test_cases_loop([{skip_case,{Case,Comment},SkipMode}|Cases],
Config, TimetrapData, Mode, Status) ->
{Mod,Func} = skip_case(user, undefined, get(test_server_case_num)+1,
- Case, Comment, is_io_buffered()),
- test_server_sup:framework_call(report, [tc_user_skip,{Mod,Func,Comment}]),
+ Case, Comment, is_io_buffered(), SkipMode),
+ test_server_sup:framework_call(report, [tc_user_skip,
+ {Mod,{Func,get_name(SkipMode)},
+ Comment}]),
run_test_cases_loop(Cases, Config, TimetrapData, Mode,
update_status(skipped, Mod, Func, Status));
@@ -2430,8 +2442,9 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
{Ref,Ref} ->
case check_props(parallel, tl(Mode0)) of
false ->
- %% this is an end conf for a top level parallel group, collect
- %% results from the test case processes and calc total time
+ %% this is an end conf for a top level parallel group,
+ %% collect results from the test case processes
+ %% and calc total time
OkSkipFail = handle_test_case_io_and_status(),
file:set_cwd(filename:dirname(get(test_server_dir))),
After = ?now,
@@ -2955,7 +2968,6 @@ get_tc_results([{_,{OkSkipFail,_}} | _Status]) ->
get_tc_results([]) -> % in case init_per_suite crashed
{[],[],[]}.
-
conf(Ref, Props) ->
{Ref,Props,?now}.
@@ -3160,10 +3172,6 @@ random_order(N, {Pos,NewSeed}, IxCases, Shuffled) ->
%% SendSync determines if start and finished messages must be sent so
%% that the printouts can be buffered and handled in order with io from
%% parallel processes.
-
-skip_case(Type, Ref, CaseNum, Case, Comment, SendSync) ->
- skip_case(Type, Ref, CaseNum, Case, Comment, SendSync, []).
-
skip_case(Type, Ref, CaseNum, Case, Comment, SendSync, Mode) ->
MF = {Mod,Func} = case Case of
{M,F,_A} -> {M,F};
@@ -3241,7 +3249,7 @@ skip_case1(Type, CaseNum, Mod, Func, Comment, Mode) ->
%% SkipType = skip_case | auto_skip_case
%% Mark all cases tagged with Ref as skipped.
-skip_cases_upto(Ref, Cases, Reason, Origin, Mode, SkipType) ->
+skip_cases_upto(Ref, Cases, Reason, Origin, Mode, SkipType) ->
{_,Modified,Rest} =
modify_cases_upto(Ref, {skip,Reason,Origin,Mode,SkipType}, Cases),
Modified++Rest.
@@ -3326,21 +3334,34 @@ modify_cases_upto1(Ref, {copy,NewRef},
{[C|Orig],[{skip_case,{Type,NewRef,MF,Cmt}}|Alt],T};
%% next is a skip_case, could be one test case or 'all' in suite, we must proceed
-modify_cases_upto1(Ref, ModOp, [{skip_case,{_F,_Cmt}}=MF|T], Orig, Alt) ->
+modify_cases_upto1(Ref, ModOp, [{skip_case,{_F,_Cmt},_Mode}=MF|T], Orig, Alt) ->
modify_cases_upto1(Ref, ModOp, T, [MF|Orig], [MF|Alt]);
%% next is a normal case (possibly in a sequence), mark as skipped, or copy, and proceed
-modify_cases_upto1(Ref, {skip,Reason,_,_,skip_case}=Op,
+modify_cases_upto1(Ref, {skip,Reason,_,Mode,skip_case}=Op,
[{_M,_F}=MF|T], Orig, Alt) ->
- modify_cases_upto1(Ref, Op, T, Orig, [{skip_case,{MF,Reason}}|Alt]);
+ modify_cases_upto1(Ref, Op, T, Orig, [{skip_case,{MF,Reason},Mode}|Alt]);
modify_cases_upto1(Ref, {skip,Reason,_,Mode,auto_skip_case}=Op,
[{_M,_F}=MF|T], Orig, Alt) ->
modify_cases_upto1(Ref, Op, T, Orig, [{auto_skip_case,{MF,Reason},Mode}|Alt]);
modify_cases_upto1(Ref, CopyOp, [{_M,_F}=MF|T], Orig, Alt) ->
modify_cases_upto1(Ref, CopyOp, T, [MF|Orig], [MF|Alt]);
+%% next is a conf case, modify the Mode arg to keep track of sub groups
+modify_cases_upto1(Ref, {skip,Reason,FType,Mode,SkipType},
+ [{conf,OtherRef,Props,_MF}|T], Orig, Alt) ->
+ case hd(Mode) of
+ {OtherRef,_,_} -> % end conf
+ modify_cases_upto1(Ref, {skip,Reason,FType,tl(Mode),SkipType},
+ T, Orig, Alt);
+ _ -> % start conf
+ Mode1 = [conf(OtherRef,Props)|Mode],
+ modify_cases_upto1(Ref, {skip,Reason,FType,Mode1,SkipType},
+ T, Orig, Alt)
+ end;
+
%% next is some other case, ignore or copy
-modify_cases_upto1(Ref, {skip,_,_,_,_}=Op, [_|T], Orig, Alt) ->
+modify_cases_upto1(Ref, {skip,_,_,_,_}=Op, [_Other|T], Orig, Alt) ->
modify_cases_upto1(Ref, Op, T, Orig, Alt);
modify_cases_upto1(Ref, CopyOp, [C|T], Orig, Alt) ->
modify_cases_upto1(Ref, CopyOp, T, [C|Orig], [C|Alt]).
@@ -3665,12 +3686,13 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
update_config(hd(Args), [{priv_dir,PrivDir++"/"},
{tc_logfile,MinorName}])
end,
-
+ GrName = get_name(Mode),
test_server_sup:framework_call(report,
- [tc_start,{{Mod,Func},MinorName}]),
+ [tc_start,{{Mod,{Func,GrName}},
+ MinorName}]),
print_props((RunInit==skip_init), get_props(Mode)),
- GroupName = case get_name(Mode) of
+ GrNameStr = case GrName of
undefined -> "";
Name -> cast_to_list(Name)
end,
@@ -3683,14 +3705,14 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
"<td>" ++ Col0 ++ "~ts" ++ Col1 ++ "</td>"
"<td><a href=\"~ts\">~w</a></td>"
"<td><a href=\"~ts#top\"><</a> <a href=\"~ts#end\">></a></td>",
- [num2str(Num),fw_name(Mod),GroupName,EncMinorBase,Func,
+ [num2str(Num),fw_name(Mod),GrNameStr,EncMinorBase,Func,
EncMinorBase,EncMinorBase]),
do_unless_parallel(Main, fun erlang:yield/0),
%% run the test case
{Result,DetectedFail,ProcsBefore,ProcsAfter} =
- run_test_case_apply(Num, Mod, Func, [UpdatedArgs], get_name(Mode),
+ run_test_case_apply(Num, Mod, Func, [UpdatedArgs], GrName,
RunInit, TimetrapData),
{Time,RetVal,Loc,Opts,Comment} =
case Result of
@@ -3709,41 +3731,41 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
Status =
case {Time,RetVal} of
{died,{timetrap_timeout,TimetrapTimeout}} ->
- progress(failed, Num, Mod, Func, Loc,
+ progress(failed, Num, Mod, Func, GrName, Loc,
timetrap_timeout, TimetrapTimeout, Comment, Style);
{died,Reason} ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',{Skip,Reason}}} when Skip==skip; Skip==skipped;
Skip==auto_skip ->
- progress(skip, Num, Mod, Func, Loc, Reason,
+ progress(skip, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',_Pid,{Skip,Reason}}} when Skip==skip; Skip==skipped ->
- progress(skip, Num, Mod, Func, Loc, Reason,
+ progress(skip, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',_Pid,Reason}} ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',Reason}} ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{Fail,Reason}} when Fail =:= fail; Fail =:= failed ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,Reason={auto_skip,_Why}} ->
- progress(skip, Num, Mod, Func, Loc, Reason,
+ progress(skip, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{_,{Skip,Reason}} when Skip==skip; Skip==skipped ->
- progress(skip, Num, Mod, Func, Loc, Reason,
+ progress(skip, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style);
{Time,RetVal} ->
case DetectedFail of
[] ->
- progress(ok, Num, Mod, Func, Loc, RetVal,
+ progress(ok, Num, Mod, Func, GrName, Loc, RetVal,
Time, Comment, Style);
Reason ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, GrName, Loc, Reason,
Time, Comment, Style)
end
end,
@@ -3848,7 +3870,7 @@ num2str(N) -> integer_to_list(N).
%% Note: Strings that are to be written to the minor log must
%% be prefixed with "=== " here, or the indentation will be wrong.
-progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
+progress(skip, CaseNum, Mod, Func, GrName, Loc, Reason, Time,
Comment, {St0,St1}) ->
{Reason1,{Color,Ret,ReportTag}} =
if_auto_skip(Reason,
@@ -3857,7 +3879,7 @@ progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
print(major, "=result ~w: ~p", [ReportTag,Reason1]),
print(1, "*** SKIPPED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
- test_server_sup:framework_call(report, [tc_done,{Mod,Func,
+ test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},
{ReportTag,Reason1}}]),
ReasonStr = reason_to_string(Reason1),
ReasonStr1 = lists:flatten([string:strip(S,left) ||
@@ -3882,13 +3904,13 @@ progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
print(minor, "=== reason = ~ts", [ReasonStr1]),
Ret;
-progress(failed, CaseNum, Mod, Func, Loc, timetrap_timeout, T,
+progress(failed, CaseNum, Mod, Func, GrName, Loc, timetrap_timeout, T,
Comment0, {St0,St1}) ->
print(major, "=result failed: timeout, ~p", [Loc]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report,
- [tc_done,{Mod,Func,
+ [tc_done,{Mod,{Func,GrName},
{failed,timetrap_timeout}}]),
FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)),
ErrorReason = io_lib:format("{timetrap_timeout,~ts}", [FormatLastLoc]),
@@ -3908,13 +3930,13 @@ progress(failed, CaseNum, Mod, Func, Loc, timetrap_timeout, T,
print(minor, "=== reason = timetrap timeout", []),
failed;
-progress(failed, CaseNum, Mod, Func, Loc, {testcase_aborted,Reason}, _T,
+progress(failed, CaseNum, Mod, Func, GrName, Loc, {testcase_aborted,Reason}, _T,
Comment0, {St0,St1}) ->
print(major, "=result failed: testcase_aborted, ~p", [Loc]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report,
- [tc_done,{Mod,Func,
+ [tc_done,{Mod,{Func,GrName},
{failed,testcase_aborted}}]),
FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)),
ErrorReason = io_lib:format("{testcase_aborted,~ts}", [FormatLastLoc]),
@@ -3934,12 +3956,12 @@ progress(failed, CaseNum, Mod, Func, Loc, {testcase_aborted,Reason}, _T,
print(minor, "=== reason = {testcase_aborted,~p}", [Reason]),
failed;
-progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
+progress(failed, CaseNum, Mod, Func, GrName, unknown, Reason, Time,
Comment0, {St0,St1}) ->
print(major, "=result failed: ~p, ~w", [Reason,unknown]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
- test_server_sup:framework_call(report, [tc_done,{Mod,Func,
+ test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},
{failed,Reason}}]),
TimeStr = io_lib:format(if is_float(Time) -> "~.3fs";
true -> "~w"
@@ -3970,12 +3992,12 @@ progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
print(minor, "=== reason = " ++ FStr, [FormattedReason]),
failed;
-progress(failed, CaseNum, Mod, Func, Loc, Reason, Time,
+progress(failed, CaseNum, Mod, Func, GrName, Loc, Reason, Time,
Comment0, {St0,St1}) ->
print(major, "=result failed: ~p, ~p", [Reason,Loc]),
print(1, "*** FAILED ~ts ***",
[get_info_str(Mod,Func, CaseNum, get(test_server_cases))]),
- test_server_sup:framework_call(report, [tc_done,{Mod,Func,
+ test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},
{failed,Reason}}]),
TimeStr = io_lib:format(if is_float(Time) -> "~.3fs";
true -> "~w"
@@ -3997,10 +4019,10 @@ progress(failed, CaseNum, Mod, Func, Loc, Reason, Time,
print(minor, "=== reason = " ++ FStr, [FormattedReason]),
failed;
-progress(ok, _CaseNum, Mod, Func, _Loc, RetVal, Time,
+progress(ok, _CaseNum, Mod, Func, GrName, _Loc, RetVal, Time,
Comment0, {St0,St1}) ->
print(minor, "successfully completed test case", []),
- test_server_sup:framework_call(report, [tc_done,{Mod,Func,ok}]),
+ test_server_sup:framework_call(report, [tc_done,{Mod,{Func,GrName},ok}]),
Comment =
case RetVal of
{comment,RetComment} ->
@@ -4489,18 +4511,18 @@ update_config(Config, []) ->
collect_all_cases(Top, Skip) when is_list(Skip) ->
Result =
- case collect_cases(Top, #cc{mod=[],skip=Skip}) of
+ case collect_cases(Top, #cc{mod=[],skip=Skip}, []) of
{ok,Cases,_St} -> Cases;
Other -> Other
end,
Result.
-collect_cases([], St) -> {ok,[],St};
-collect_cases([Case|Cs0], St0) ->
- case collect_cases(Case, St0) of
+collect_cases([], St, _) -> {ok,[],St};
+collect_cases([Case|Cs0], St0, Mode) ->
+ case collect_cases(Case, St0, Mode) of
{ok,FlatCases1,St1} ->
- case collect_cases(Cs0, St1) of
+ case collect_cases(Cs0, St1, Mode) of
{ok,FlatCases2,St} ->
{ok,FlatCases1 ++ FlatCases2,St};
{error,_Reason} = Error -> Error
@@ -4509,39 +4531,41 @@ collect_cases([Case|Cs0], St0) ->
end;
-collect_cases({module,Case}, St) when is_atom(Case), is_atom(St#cc.mod) ->
- collect_case({St#cc.mod,Case}, St);
-collect_cases({module,Mod,Case}, St) ->
- collect_case({Mod,Case}, St);
-collect_cases({module,Mod,Case,Args}, St) ->
- collect_case({Mod,Case,Args}, St);
-
-collect_cases({dir,SubDir}, St) ->
- collect_files(SubDir, "*_SUITE", St);
-collect_cases({dir,SubDir,Pattern}, St) ->
- collect_files(SubDir, Pattern++"*", St);
-
-collect_cases({conf,InitF,CaseList,FinMF}, St) when is_atom(InitF) ->
- collect_cases({conf,[],{St#cc.mod,InitF},CaseList,FinMF}, St);
-collect_cases({conf,InitMF,CaseList,FinF}, St) when is_atom(FinF) ->
- collect_cases({conf,[],InitMF,CaseList,{St#cc.mod,FinF}}, St);
-collect_cases({conf,InitMF,CaseList,FinMF}, St0) ->
- collect_cases({conf,[],InitMF,CaseList,FinMF}, St0);
-collect_cases({conf,Props,InitF,CaseList,FinMF}, St) when is_atom(InitF) ->
+collect_cases({module,Case}, St, Mode) when is_atom(Case), is_atom(St#cc.mod) ->
+ collect_case({St#cc.mod,Case}, St, Mode);
+collect_cases({module,Mod,Case}, St, Mode) ->
+ collect_case({Mod,Case}, St, Mode);
+collect_cases({module,Mod,Case,Args}, St, Mode) ->
+ collect_case({Mod,Case,Args}, St, Mode);
+
+collect_cases({dir,SubDir}, St, Mode) ->
+ collect_files(SubDir, "*_SUITE", St, Mode);
+collect_cases({dir,SubDir,Pattern}, St, Mode) ->
+ collect_files(SubDir, Pattern++"*", St, Mode);
+
+collect_cases({conf,InitF,CaseList,FinMF}, St, Mode) when is_atom(InitF) ->
+ collect_cases({conf,[],{St#cc.mod,InitF},CaseList,FinMF}, St, Mode);
+collect_cases({conf,InitMF,CaseList,FinF}, St, Mode) when is_atom(FinF) ->
+ collect_cases({conf,[],InitMF,CaseList,{St#cc.mod,FinF}}, St, Mode);
+collect_cases({conf,InitMF,CaseList,FinMF}, St0, Mode) ->
+ collect_cases({conf,[],InitMF,CaseList,FinMF}, St0, Mode);
+collect_cases({conf,Props,InitF,CaseList,FinMF}, St, Mode) when is_atom(InitF) ->
case init_props(Props) of
{error,_} ->
{ok,[],St};
Props1 ->
- collect_cases({conf,Props1,{St#cc.mod,InitF},CaseList,FinMF}, St)
+ collect_cases({conf,Props1,{St#cc.mod,InitF},CaseList,FinMF},
+ St, Mode)
end;
-collect_cases({conf,Props,InitMF,CaseList,FinF}, St) when is_atom(FinF) ->
+collect_cases({conf,Props,InitMF,CaseList,FinF}, St, Mode) when is_atom(FinF) ->
case init_props(Props) of
{error,_} ->
{ok,[],St};
Props1 ->
- collect_cases({conf,Props1,InitMF,CaseList,{St#cc.mod,FinF}}, St)
+ collect_cases({conf,Props1,InitMF,CaseList,{St#cc.mod,FinF}},
+ St, Mode)
end;
-collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) ->
+collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St, Mode) ->
case init_props(Props) of
{error,_} ->
{ok,[],St};
@@ -4549,13 +4573,13 @@ collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) ->
Ref = make_ref(),
Skips = St#cc.skip,
Props2 = [{suite,St#cc.mod} | lists:delete(suite,Props1)],
- Mode = [{Ref,Props2,undefined}],
+ Mode1 = [{Ref,Props2,undefined} | Mode],
case in_skip_list({St#cc.mod,Conf}, Skips) of
{true,Comment} -> % conf init skipped
- {ok,[{skip_case,{conf,Ref,InitMF,Comment},Mode} |
+ {ok,[{skip_case,{conf,Ref,InitMF,Comment},Mode1} |
[] ++ [{conf,Ref,[],FinMF}]],St};
{true,Name,Comment} when is_atom(Name) -> % all cases skipped
- case collect_cases(CaseList, St) of
+ case collect_cases(CaseList, St, Mode1) of
{ok,[],_St} = Empty ->
Empty;
{ok,FlatCases,St1} ->
@@ -4563,15 +4587,15 @@ collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) ->
keep_name(Props1),
FinMF}],
Skipped = skip_cases_upto(Ref, Cases2Skip, Comment,
- conf, Mode, skip_case),
- {ok,[{skip_case,{conf,Ref,InitMF,Comment},Mode} |
+ conf, Mode1, skip_case),
+ {ok,[{skip_case,{conf,Ref,InitMF,Comment},Mode1} |
Skipped],St1};
{error,_Reason} = Error ->
Error
end;
{true,ToSkip,_} when is_list(ToSkip) -> % some cases skipped
case collect_cases(CaseList,
- St#cc{skip=ToSkip++Skips}) of
+ St#cc{skip=ToSkip++Skips}, Mode1) of
{ok,[],_St} = Empty ->
Empty;
{ok,FlatCases,St1} ->
@@ -4583,7 +4607,7 @@ collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) ->
Error
end;
false ->
- case collect_cases(CaseList, St) of
+ case collect_cases(CaseList, St, Mode1) of
{ok,[],_St} = Empty ->
Empty;
{ok,FlatCases,St1} ->
@@ -4597,8 +4621,8 @@ collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) ->
end
end;
-collect_cases({make,InitMFA,CaseList,FinMFA}, St0) ->
- case collect_cases(CaseList, St0) of
+collect_cases({make,InitMFA,CaseList,FinMFA}, St0, Mode) ->
+ case collect_cases(CaseList, St0, Mode) of
{ok,[],_St} = Empty -> Empty;
{ok,FlatCases,St} ->
Ref = make_ref(),
@@ -4607,62 +4631,62 @@ collect_cases({make,InitMFA,CaseList,FinMFA}, St0) ->
{error,_Reason} = Error -> Error
end;
-collect_cases({Module, Cases}, St) when is_list(Cases) ->
- case (catch collect_case(Cases, St#cc{mod=Module}, [])) of
+collect_cases({Module, Cases}, St, Mode) when is_list(Cases) ->
+ case (catch collect_case(Cases, St#cc{mod=Module}, [], Mode)) of
{ok, NewCases, NewSt} ->
{ok, NewCases, NewSt};
Other ->
{error, Other}
end;
-collect_cases({_Mod,_Case}=Spec, St) ->
- collect_case(Spec, St);
+collect_cases({_Mod,_Case}=Spec, St, Mode) ->
+ collect_case(Spec, St, Mode);
-collect_cases({_Mod,_Case,_Args}=Spec, St) ->
- collect_case(Spec, St);
-collect_cases(Case, St) when is_atom(Case), is_atom(St#cc.mod) ->
- collect_case({St#cc.mod,Case}, St);
-collect_cases(Other, St) ->
+collect_cases({_Mod,_Case,_Args}=Spec, St, Mode) ->
+ collect_case(Spec, St, Mode);
+collect_cases(Case, St, Mode) when is_atom(Case), is_atom(St#cc.mod) ->
+ collect_case({St#cc.mod,Case}, St, Mode);
+collect_cases(Other, St, _Mode) ->
{error,{bad_subtest_spec,St#cc.mod,Other}}.
-collect_case({Mod,{conf,_,_,_,_}=Conf}, St) ->
- collect_case_invoke(Mod, Conf, [], St);
+collect_case({Mod,{conf,_,_,_,_}=Conf}, St, Mode) ->
+ collect_case_invoke(Mod, Conf, [], St, Mode);
-collect_case(MFA, St) ->
+collect_case(MFA, St, Mode) ->
case in_skip_list(MFA, St#cc.skip) of
{true,Comment} ->
- {ok,[{skip_case,{MFA,Comment}}],St};
+ {ok,[{skip_case,{MFA,Comment},Mode}],St};
false ->
case MFA of
- {Mod,Case} -> collect_case_invoke(Mod, Case, MFA, St);
+ {Mod,Case} -> collect_case_invoke(Mod, Case, MFA, St, Mode);
{_Mod,_Case,_Args} -> {ok,[MFA],St}
end
end.
-collect_case([], St, Acc) ->
+collect_case([], St, Acc, _Mode) ->
{ok, Acc, St};
-collect_case([Case | Cases], St, Acc) ->
- {ok, FlatCases, NewSt} = collect_case({St#cc.mod, Case}, St),
- collect_case(Cases, NewSt, Acc ++ FlatCases).
+collect_case([Case | Cases], St, Acc, Mode) ->
+ {ok, FlatCases, NewSt} = collect_case({St#cc.mod, Case}, St, Mode),
+ collect_case(Cases, NewSt, Acc ++ FlatCases, Mode).
-collect_case_invoke(Mod, Case, MFA, St) ->
+collect_case_invoke(Mod, Case, MFA, St, Mode) ->
case get_fw_mod(undefined) of
undefined ->
case catch apply(Mod, Case, [suite]) of
{'EXIT',_} ->
{ok,[MFA],St};
Suite ->
- collect_subcases(Mod, Case, MFA, St, Suite)
+ collect_subcases(Mod, Case, MFA, St, Suite, Mode)
end;
_ ->
Suite = test_server_sup:framework_call(get_suite,
[Mod,Case],
[]),
- collect_subcases(Mod, Case, MFA, St, Suite)
+ collect_subcases(Mod, Case, MFA, St, Suite, Mode)
end.
-collect_subcases(Mod, Case, MFA, St, Suite) ->
+collect_subcases(Mod, Case, MFA, St, Suite, Mode) ->
case Suite of
[] when Case == all -> {ok,[],St};
[] when element(1, Case) == conf -> {ok,[],St};
@@ -4670,28 +4694,28 @@ collect_subcases(Mod, Case, MFA, St, Suite) ->
%%%! --- START Kept for backwards compatibility ---
%%%! Requirements are not used
{req,ReqList} ->
- collect_case_deny(Mod, Case, MFA, ReqList, [], St);
+ collect_case_deny(Mod, Case, MFA, ReqList, [], St, Mode);
{req,ReqList,SubCases} ->
- collect_case_deny(Mod, Case, MFA, ReqList, SubCases, St);
+ collect_case_deny(Mod, Case, MFA, ReqList, SubCases, St, Mode);
%%%! --- END Kept for backwards compatibility ---
{Skip,Reason} when Skip==skip; Skip==skipped ->
- {ok,[{skip_case,{MFA,Reason}}],St};
+ {ok,[{skip_case,{MFA,Reason},Mode}],St};
{error,Reason} ->
throw(Reason);
SubCases ->
- collect_case_subcases(Mod, Case, SubCases, St)
+ collect_case_subcases(Mod, Case, SubCases, St, Mode)
end.
-collect_case_subcases(Mod, Case, SubCases, St0) ->
+collect_case_subcases(Mod, Case, SubCases, St0, Mode) ->
OldMod = St0#cc.mod,
- case collect_cases(SubCases, St0#cc{mod=Mod}) of
+ case collect_cases(SubCases, St0#cc{mod=Mod}, Mode) of
{ok,FlatCases,St} ->
{ok,FlatCases,St#cc{mod=OldMod}};
{error,Reason} ->
{error,{{Mod,Case},Reason}}
end.
-collect_files(Dir, Pattern, St) ->
+collect_files(Dir, Pattern, St, Mode) ->
{ok,Cwd} = file:get_cwd(),
Dir1 = filename:join(Cwd, Dir),
Wc = filename:join([Dir1,Pattern++code:objfile_extension()]),
@@ -4701,7 +4725,7 @@ collect_files(Dir, Pattern, St) ->
{error,{collect_fail,Dir,Pattern}};
Mods0 ->
Mods = [{path_to_module(Mod),all} || Mod <- lists:sort(Mods0)],
- collect_cases(Mods, St)
+ collect_cases(Mods, St, Mode)
end.
path_to_module(Path) when is_list(Path) ->
@@ -4711,14 +4735,14 @@ path_to_module(Path) when is_list(Path) ->
%% anyway. It should be removed or renamed!
list_to_atom(filename:rootname(filename:basename(Path))).
-collect_case_deny(Mod, Case, MFA, ReqList, SubCases, St) ->
+collect_case_deny(Mod, Case, MFA, ReqList, SubCases, St, Mode) ->
case {check_deny(ReqList, St#cc.skip),SubCases} of
{{denied,Comment},_SubCases} ->
- {ok,[{skip_case,{MFA,Comment}}],St};
+ {ok,[{skip_case,{MFA,Comment},Mode}],St};
{granted,[]} ->
{ok,[MFA],St};
{granted,SubCases} ->
- collect_case_subcases(Mod, Case, SubCases, St)
+ collect_case_subcases(Mod, Case, SubCases, St, Mode)
end.
check_deny([Req|Reqs], DenyList) ->
@@ -5476,10 +5500,16 @@ write_html_file(File,Content) ->
%% The 'major' log file, which is a pure text file is also written
%% with utf8 encoding
open_utf8_file(File) ->
- file:open(File,[write,{encoding,utf8}]).
+ case file:open(File,AllOpts=[write,{encoding,utf8}]) of
+ {error,Reason} -> {error,{Reason,{File,AllOpts}}};
+ Result -> Result
+ end.
open_utf8_file(File,Opts) ->
- file:open(File,[{encoding,utf8}|Opts]).
+ case file:open(File,AllOpts=[{encoding,utf8}|Opts]) of
+ {error,Reason} -> {error,{Reason,{File,AllOpts}}};
+ Result -> Result
+ end.
%% Write a file with specified encoding
write_file(File,Content,latin1) ->
diff --git a/lib/test_server/src/test_server_node.erl b/lib/test_server/src/test_server_node.erl
index 582abb2153..acd47788db 100644
--- a/lib/test_server/src/test_server_node.erl
+++ b/lib/test_server/src/test_server_node.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -653,7 +653,7 @@ find_rel_linux(Rel) ->
end.
find_rel_suse(Rel, SuseRel) ->
- Root = "/usr/local/otp/releases/otp_beam_linux_sles",
+ Root = "/usr/local/otp/releases/sles",
case SuseRel of
"11" ->
%% Try both SuSE 11, SuSE 10 and SuSe 9 in that order.
@@ -673,10 +673,10 @@ find_rel_suse(Rel, SuseRel) ->
find_rel_suse_1(Rel, RootWc) ->
case erlang:system_info(wordsize) of
4 ->
- find_rel_suse_2(Rel, RootWc++"_i386");
+ find_rel_suse_2(Rel, RootWc++"_32");
8 ->
- find_rel_suse_2(Rel, RootWc++"_x64") ++
- find_rel_suse_2(Rel, RootWc++"_i386")
+ find_rel_suse_2(Rel, RootWc++"_64") ++
+ find_rel_suse_2(Rel, RootWc++"_32")
end.
find_rel_suse_2(Rel, RootWc) ->
diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl
index 3cfa84a52f..96e369a138 100644
--- a/lib/test_server/src/test_server_sup.erl
+++ b/lib/test_server/src/test_server_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -469,7 +469,7 @@ check_instruction(_, {add_application, Application, Type}, _, _) ->
check_instruction(_, Instr, _, _) ->
throw({error, {low_level_or_invalid_instruction, Instr}}).
-check_module(Module, Modules) when is_atom(Module) ->
+check_module(Module, Modules) ->
case {is_atom(Module), lists:member(Module, Modules)} of
{true, true} -> ok;
{true, false} -> throw({error, {unknown_module, Module}});
diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl
index 11d6f7af4d..bc7d244c7c 100644
--- a/lib/test_server/src/ts.erl
+++ b/lib/test_server/src/ts.erl
@@ -24,7 +24,7 @@
-module(ts).
--export([run/0, run/1, run/2, run/3, run/4,
+-export([run/0, run/1, run/2, run/3, run/4, run/5,
tests/0, tests/1,
install/0, install/1,
bench/0, bench/1, bench/2, benchmarks/0,
@@ -389,6 +389,16 @@ run(Testspec, Mod, Grs={group,_Groups}, Config) when is_atom(Testspec),
Args = [{suite,Mod},Grs],
run_test(atom_to_list(Testspec), Args, Options).
+%% run/5
+%% Run one or more test cases in a group with Options.
+run(Testspec, Mod, Group, Cases, Config) when is_atom(Testspec),
+ is_atom(Mod),
+ is_list(Config) ->
+ Group1 = if is_tuple(Group) -> Group; true -> {group,Group} end,
+ Cases1 = if is_tuple(Cases) -> Cases; true -> {testcase,Cases} end,
+ Options=check_test_get_opts(Testspec, Config),
+ Args = [{suite,Mod},Group1,Cases1],
+ run_test(atom_to_list(Testspec), Args, Options).
is_list_of_suites(List) ->
lists:all(fun(Suite) ->
diff --git a/lib/test_server/src/ts.unix.config b/lib/test_server/src/ts.unix.config
index a34857b9e5..1ba5d9033e 100644
--- a/lib/test_server/src/ts.unix.config
+++ b/lib/test_server/src/ts.unix.config
@@ -3,4 +3,4 @@
%% Always run a (VNC) X server on host
%% {xserver, "xserver.example.com:66"}.
-{unix,[{telnet,"belegost"},{username,"bofh"},{password,"root"},{keep_alive,true}]}.
+{unix,[{telnet,"belegost"},{username,"telnet-test"},{password,"tset-tenlet"},{keep_alive,true}]}.
diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl
index d96abfc55a..18d021f780 100644
--- a/lib/test_server/src/ts_run.erl
+++ b/lib/test_server/src/ts_run.erl
@@ -398,8 +398,9 @@ make_common_test_args(Args0, Options0, _Vars) ->
end,
ConfigFiles = [{config,[filename:join(ConfigPath,File)
|| File <- get_config_files()]}],
- io_lib:format("~100000p",[Args0++Trace++Cover++Logdir++
- ConfigFiles++Options++TimeTrap]).
+ io_lib:format("~100000p",[[{abort_if_missing_suites,true} |
+ Args0++Trace++Cover++Logdir++
+ ConfigFiles++Options++TimeTrap]]).
to_list(X) when is_atom(X) ->
atom_to_list(X);
diff --git a/lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl
index f38f768f3b..9658191289 100644
--- a/lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl
@@ -191,7 +191,7 @@ conf1_end(Config) ->
%% check 2s & 3s < 4s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 3500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 4000000 -> exit({bad_parallel_exec,Ms});
Ms < 3000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -204,7 +204,7 @@ conf2_end(Config) ->
%% check 3s & 2s < 4s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 3500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 4000000 -> exit({bad_parallel_exec,Ms});
Ms < 3000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -217,7 +217,7 @@ conf3_end(Config) ->
%% check 6s & 6s & (2s & 3s) & 1s = ~6s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 6500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 7000000 -> exit({bad_parallel_exec,Ms});
Ms < 6000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -230,7 +230,7 @@ conf4_end(Config) ->
%% check 2s & 3s >= 5s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 5500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 6000000 -> exit({bad_parallel_exec,Ms});
Ms < 5000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -243,7 +243,7 @@ conf5_end(Config) ->
%% check 1s & 1s & (3s & 2s) & 1s = ~6s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 7000000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 7500000 -> exit({bad_parallel_exec,Ms});
Ms < 6000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -257,7 +257,7 @@ conf6_end(Config) ->
%% check 3s & 2s < 5s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 3500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 4500000 -> exit({bad_parallel_exec,Ms});
Ms < 3000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -277,7 +277,7 @@ conf7_end(Config) ->
%% check 1s & 1s & (2s & 2s) & 1s = ~3s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 3500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 4000000 -> exit({bad_parallel_exec,Ms});
Ms < 3000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
@@ -291,7 +291,7 @@ conf8_end(Config) ->
%% check 2s & 2s < 4s
Ms = timer:now_diff(now(),?config(t0,Config)),
test_server:comment(io_lib:format("~p",[now()])),
- if Ms > 2500000 -> exit({bad_parallel_exec,Ms});
+ if Ms > 3000000 -> exit({bad_parallel_exec,Ms});
Ms < 2000000 -> exit({bad_parallel_exec,Ms});
true -> ok
end.
diff --git a/lib/test_server/vsn.mk b/lib/test_server/vsn.mk
index 6871b5bd14..4eb70aa2cd 100644
--- a/lib/test_server/vsn.mk
+++ b/lib/test_server/vsn.mk
@@ -1 +1 @@
-TEST_SERVER_VSN = 3.6.4
+TEST_SERVER_VSN = 3.7
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index 2e4c354fbd..136e0a3127 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -30,6 +30,82 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.6.14</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Removed the support for the query keyword from emacs mode
+ (Thanks to Paul Oliver)</p>
+ <p>
+ Own Id: OTP-11568</p>
+ </item>
+ <item>
+ <p>
+ Emacs mode improvements (Thanks to Steve Vinoski)</p>
+ <p>
+ Own Id: OTP-11601</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ <item>
+ <p>
+ The emacs erlang mode now match erlang keywords more
+ carefully (Thanks to Steve Vinoski)</p>
+ <p>
+ Own Id: OTP-11786</p>
+ </item>
+ <item>
+ <p>
+ The emacs erlang-mode now auto loads for more file types
+ (Thanks to Phil Hagelberg)</p>
+ <p>
+ Own Id: OTP-11788</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ <c>cover</c> can run on itself. Also, support for reading
+ BEAM files produced by ancient OTP versions before R9C
+ has been removed.</p>
+ <p>
+ Own Id: OTP-11692</p>
+ </item>
+ <item>
+ <p>
+ Support maps in cover</p>
+ <p>
+ Own Id: OTP-11764</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.6.13</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index f007f780eb..4e3c49c717 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -853,7 +853,6 @@ resulting regexp is surrounded by \\_< and \\_>."
"append_element"
"await_proc_exit"
"await_sched_wall_time_modifications"
- "bitstr_to_list"
"bump_reductions"
"call_on_load_function"
"cancel_timer"
@@ -899,7 +898,6 @@ resulting regexp is surrounded by \\_< and \\_>."
"hibernate"
"insert_element"
"is_builtin"
- "list_to_bitstr"
"load_nif"
"loaded"
"localtime"
@@ -1420,6 +1418,10 @@ Other commands:
(if (boundp 'after-change-major-mode-hook)
(run-hooks 'after-change-major-mode-hook)))
+;;;###autoload
+(dolist (r '("\\.erl$" "\\.app\\.src$" "\\.escript"
+ "\\.hrl$" "\\.xrl$" "\\.yrl" "/ebin/.+\\.app"))
+ (add-to-list 'auto-mode-alist (cons r 'erlang-mode)))
(defun erlang-syntax-table-init ()
(if (null erlang-mode-syntax-table)
@@ -2570,9 +2572,9 @@ Value is list (stack token-start token-type in-what)."
(erlang-pop stack))
(if (and stack (memq (car (car stack)) '(icr begin fun try)))
(erlang-pop stack))))
- ((looking-at "catch.*of")
+ ((looking-at "catch\\b.*of")
t)
- ((looking-at "catch\\s *\\($\\|%\\|.*->\\)")
+ ((looking-at "catch\\b\\s *\\($\\|%\\|.*->\\)")
;; Must pop top icr layer, `catch' in try/catch
;;will push a new layer next.
(progn
@@ -2620,9 +2622,9 @@ Value is list (stack token-start token-type in-what)."
;;((looking-at "when\\s *\\($\\|%\\)")
((looking-at "when[^_a-zA-Z0-9]")
(erlang-push (list 'when token (current-column)) stack))
- ((looking-at "catch.*of")
+ ((looking-at "catch\\b.*of")
t)
- ((looking-at "catch\\s *\\($\\|%\\|.*->\\)")
+ ((looking-at "catch\\b\\s *\\($\\|%\\|.*->\\)")
(erlang-push (list 'icr token (current-column)) stack))
;;(erlang-push (list '-> token (current-column)) stack))
;;((looking-at "^of$")
@@ -2913,7 +2915,7 @@ Return nil if inside string, t if in a comment."
(if stack
(erlang-caddr (car stack))
0))
- ((looking-at "catch\\($\\|[^_a-zA-Z0-9]\\)")
+ ((looking-at "catch\\b\\($\\|[^_a-zA-Z0-9]\\)")
;; Are we in a try
(let ((start (if (eq (car stack-top) '->)
(car (cdr stack))
@@ -3124,12 +3126,12 @@ This assumes that the preceding expression is either simple
(defun erlang-at-keyword ()
"Are we looking at an Erlang keyword which will increase indentation?"
(looking-at (concat "\\(when\\|if\\|fun\\|case\\|begin\\|"
- "of\\|receive\\|after\\|catch\\|try\\)[^_a-zA-Z0-9]")))
+ "of\\|receive\\|after\\|catch\\|try\\)\\b")))
(defun erlang-at-operator ()
"Are we looking at an Erlang operator?"
(looking-at
- "\\(bnot\\|div\\|mod\\|band\\|bor\\|bxor\\|bsl\\|bsr\\)[^_a-zA-Z0-9]"))
+ "\\(bnot\\|div\\|mod\\|band\\|bor\\|bxor\\|bsl\\|bsr\\)\\b"))
(defun erlang-comment-indent ()
"Compute Erlang comment indentation.
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
index 0dc1b47f0d..1c1086ca58 100644
--- a/lib/tools/emacs/test.erl.indented
+++ b/lib/tools/emacs/test.erl.indented
@@ -749,3 +749,14 @@ commas_first() ->
%% this used to result in a scan-sexp error
[{
}].
+
+%% this used to result in 2x the correct indentation within the function
+%% body, due to the function name being mistaken for a keyword
+catcher(N) ->
+ try generate_exception(N) of
+ Val -> {N, normal, Val}
+ catch
+ throw:X -> {N, caught, thrown, X};
+ exit:X -> {N, caught, exited, X};
+ error:X -> {N, caught, error, X}
+ end.
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
index c7d2dc4ce5..a9d09000d2 100644
--- a/lib/tools/emacs/test.erl.orig
+++ b/lib/tools/emacs/test.erl.orig
@@ -749,3 +749,14 @@ commas_first() ->
%% this used to result in a scan-sexp error
[{
}].
+
+%% this used to result in 2x the correct indentation within the function
+%% body, due to the function name being mistaken for a keyword
+catcher(N) ->
+try generate_exception(N) of
+Val -> {N, normal, Val}
+catch
+throw:X -> {N, caught, thrown, X};
+exit:X -> {N, caught, exited, X};
+error:X -> {N, caught, error, X}
+end.
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index a24d70ed92..113fa24bd5 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -1764,6 +1764,25 @@ munge_expr({record,Line,Arg,Name,Exprs}, Vars) ->
munge_expr({record_field,Line,ExprL,ExprR}, Vars) ->
{MungedExprR, Vars2} = munge_expr(ExprR, Vars),
{{record_field,Line,ExprL,MungedExprR}, Vars2};
+munge_expr({map,Line,Fields}, Vars) ->
+ %% EEP 43
+ {MungedFields, Vars2} = munge_exprs(Fields, Vars, []),
+ {{map,Line,MungedFields}, Vars2};
+munge_expr({map,Line,Arg,Fields}, Vars) ->
+ %% EEP 43
+ {MungedArg, Vars2} = munge_expr(Arg, Vars),
+ {MungedFields, Vars3} = munge_exprs(Fields, Vars2, []),
+ {{map,Line,MungedArg,MungedFields}, Vars3};
+munge_expr({map_field_assoc,Line,Name,Value}, Vars) ->
+ %% EEP 43
+ {MungedName, Vars2} = munge_expr(Name, Vars),
+ {MungedValue, Vars3} = munge_expr(Value, Vars2),
+ {{map_field_assoc,Line,MungedName,MungedValue}, Vars3};
+munge_expr({map_field_exact,Line,Name,Value}, Vars) ->
+ %% EEP 43
+ {MungedName, Vars2} = munge_expr(Name, Vars),
+ {MungedValue, Vars3} = munge_expr(Value, Vars2),
+ {{map_field_exact,Line,MungedName,MungedValue}, Vars3};
munge_expr({cons,Line,ExprH,ExprT}, Vars) ->
{MungedExprH, Vars2} = munge_expr(ExprH, Vars),
{MungedExprT, Vars3} = munge_expr(ExprT, Vars2),
diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src
index 553c5eb96b..ec5b6f3a82 100644
--- a/lib/tools/src/tools.app.src
+++ b/lib/tools/src/tools.app.src
@@ -39,23 +39,9 @@
{applications, [kernel, stdlib]},
{env, [{file_util_search_methods,[{"", ""}, {"ebin", "esrc"}, {"ebin", "src"}]}
]
- }
+ },
+ {runtime_dependencies, ["webtool-0.8.10","stdlib-2.0","runtime_tools-1.8.14",
+ "kernel-3.0","inets-5.10","erts-6.0",
+ "compiler-5.0"]}
]
}.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index ec61c57cec..80807b1d38 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -516,13 +516,11 @@ reconnect(Config) ->
cover:flush(N1),
rpc:call(N1,f,f1,[]),
- %% This will cause a call to f:f2() when nodes()==[] on N1
+ %% This will cause first casue the N1 node to initiate a
+ %% disconnect and then call f:f2() when nodes() =:= [] on N1.
rpc:cast(N1,f,call_f2_when_isolated,[]),
-
- %% Disconnect and check that node is removed from main cover node
- net_kernel:disconnect(N1),
timer:sleep(500), % allow some to detect disconnect and for f:f2() call
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
%% Do some add one module (b) and remove one module (a)
code:purge(a),
@@ -530,7 +528,7 @@ reconnect(Config) ->
{ok,b} = cover:compile(b),
cover_compiled = code:which(b),
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
check_f_calls(1,0), % only the first call - before the flush
%% Reconnect the node and check that b and f are cover compiled but not a
@@ -573,7 +571,7 @@ die_and_reconnect(Config) ->
%% Kill the node
rpc:call(N1,erlang,halt,[]),
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
check_f_calls(1,0), % only the first call - before the flush
@@ -614,7 +612,7 @@ dont_reconnect_after_stop(Config) ->
%% Stop cover on the node, then terminate the node
cover:stop(N1),
rpc:call(N1,erlang,halt,[]),
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
check_f_calls(1,0),
@@ -622,7 +620,7 @@ dont_reconnect_after_stop(Config) ->
{ok,N1} = ?t:start_node(NodeName,peer,
[{args," -pa " ++ DataDir},{start_cover,false}]),
timer:sleep(300),
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
Beam = rpc:call(N1,code,which,[f]),
false = (Beam==cover_compiled),
@@ -667,7 +665,7 @@ stop_node_after_disconnect(Config) ->
{ok,N1} = ?t:start_node(NodeName,peer,
[{args," -pa " ++ DataDir},{start_cover,false}]),
timer:sleep(300),
- [] = cover:which_nodes(),
+ cover_which_nodes([]),
Beam = rpc:call(N1,code,which,[f]),
false = (Beam==cover_compiled),
@@ -1575,3 +1573,21 @@ is_unloaded(What) ->
check_f_calls(F1,F2) ->
{ok,[{{f,f1,0},F1},{{f,f2,0},F2}|_]} = cover:analyse(f,calls,function).
+
+cover_which_nodes(Expected) ->
+ case cover:which_nodes() of
+ Expected ->
+ ok;
+ Other ->
+ {Time,ok} = timer:tc(fun Retry() ->
+ case cover:which_nodes() of
+ Expected -> ok;
+ _ ->
+ ?t:sleep(100),
+ Retry()
+ end
+ end),
+ io:format("~p ms before cover:which_nodes() returned ~p",
+ [Time,Expected]),
+ Expected = Other
+ end.
diff --git a/lib/tools/test/cover_SUITE_data/f.erl b/lib/tools/test/cover_SUITE_data/f.erl
index ce2963014a..a29a67b388 100644
--- a/lib/tools/test/cover_SUITE_data/f.erl
+++ b/lib/tools/test/cover_SUITE_data/f.erl
@@ -10,10 +10,15 @@ f2() ->
f2_line2.
call_f2_when_isolated() ->
+ [Other] = nodes(),
+ net_kernel:disconnect(Other),
+ do_call_f2_when_isolated().
+
+do_call_f2_when_isolated() ->
case nodes() of
[] ->
f2();
_ ->
timer:sleep(100),
- call_f2_when_isolated()
+ do_call_f2_when_isolated()
end.
diff --git a/lib/tools/test/eprof_SUITE.erl b/lib/tools/test/eprof_SUITE.erl
index 1227d5b841..04b522de4a 100644
--- a/lib/tools/test/eprof_SUITE.erl
+++ b/lib/tools/test/eprof_SUITE.erl
@@ -104,7 +104,7 @@ basic(Config) when is_list(Config) ->
profiling = eprof:profile([A]),
true = exit(A, kill_it),
profiling_stopped = eprof:stop_profiling(),
- {error,_} = eprof:profile(fun() -> a = b end),
+ {error,_} = eprof:profile(fun() -> a = id(b) end),
%% with mfa
@@ -149,8 +149,7 @@ basic_option_1(Config) ->
% vanilla
{ok, _} = eprof:profile(fun() -> eprof_test:do(10) end, [{set_on_spawn, true}]),
- [{_, MfasDo1},{_, MfasLists1}] = eprof:dump(),
- Mfas1 = MfasDo1 ++ MfasLists1,
+ Mfas1 = lists:foldl(fun({_,Mfas},Out) -> Mfas ++ Out end, [], eprof:dump()),
{value, {_, {11, _}}} = lists:keysearch({eprof_test,dec,1}, 1, Mfas1),
{value, {_, { 1, _}}} = lists:keysearch({eprof_test, go,1}, 1, Mfas1),
@@ -159,8 +158,7 @@ basic_option_1(Config) ->
{ok, _} = eprof:profile(fun() -> eprof_test:do(10) end, [set_on_spawn]),
- [{_, MfasDo2},{_, MfasLists2}] = eprof:dump(),
- Mfas2 = MfasDo2 ++ MfasLists2,
+ Mfas2 = lists:foldl(fun({_,Mfas},Out) -> Mfas ++ Out end, [], eprof:dump()),
{value, {_, {11, _}}} = lists:keysearch({eprof_test,dec,1}, 1, Mfas2),
{value, {_, { 1, _}}} = lists:keysearch({eprof_test, go,1}, 1, Mfas2),
{value, {_, { 9, _}}} = lists:keysearch({lists, split_2,5}, 1, Mfas2),
@@ -255,3 +253,5 @@ ensure_eprof_stopped() ->
Pid ->
stopped=eprof:stop()
end.
+
+id(I) -> I.
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index 353275ae3b..6870aefe5c 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -1098,7 +1098,6 @@ read_expected(Version) ->
{POS1+1,{FF,{mod17,fun17,0}}},
{POS1+2,{FF,{erlang,spawn,1}}},
{POS1+2,{FF,{read,local,0}}},
- {POS1+3,{FF,{erlang,binary_to_term,1}}},
{POS1+3,{FF,{erlang,spawn,1}}},
{POS1+4,{FF,{dist,func,0}}},
{POS1+4,{FF,{erlang,spawn,1}}},
@@ -1207,6 +1206,7 @@ read_expected(Version) ->
OKB1 = [{POS13+1,{FF,{erts_debug,apply,4}}},
{POS13+2,{FF,{erts_debug,apply,4}}},
{POS13+3,{FF,{erts_debug,apply,4}}},
+ {POS1+3, {FF,{erlang,binary_to_term,1}}},
{POS3+1, {FF,{erlang,spawn,3}}},
{POS3+2, {FF,{erlang,spawn,3}}},
{POS3+3, {FF,{erlang,spawn_link,3}}},
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 0cead00554..2d2970de3a 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.6.13
+TOOLS_VSN = 2.6.14
diff --git a/lib/typer/Makefile b/lib/typer/Makefile
index 40a82e9bba..d4396abc9d 100644
--- a/lib/typer/Makefile
+++ b/lib/typer/Makefile
@@ -29,7 +29,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# Macros
#
-SUB_DIRECTORIES = src
+SUB_DIRECTORIES = src doc/src
include vsn.mk
VSN = $(TYPER_VSN)
diff --git a/lib/typer/doc/Makefile b/lib/typer/doc/Makefile
new file mode 100644
index 0000000000..4ea0137202
--- /dev/null
+++ b/lib/typer/doc/Makefile
@@ -0,0 +1,39 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2006-2012. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+SHELL=/bin/sh
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+clean:
+ -rm -f *.html edoc-info stylesheet.css erlang.png
+
+distclean: clean
+realclean: clean
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
diff --git a/lib/typer/doc/html/.gitignore b/lib/typer/doc/html/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/typer/doc/html/.gitignore
diff --git a/lib/typer/doc/pdf/.gitignore b/lib/typer/doc/pdf/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/typer/doc/pdf/.gitignore
diff --git a/lib/typer/doc/src/Makefile b/lib/typer/doc/src/Makefile
new file mode 100644
index 0000000000..2683c08679
--- /dev/null
+++ b/lib/typer/doc/src/Makefile
@@ -0,0 +1,117 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2006-2012. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(TYPER_VSN)
+APPLICATION=typer
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+XML_APPLICATION_FILES = ref_man.xml
+XML_REF3_FILES =
+
+XML_PART_FILES = part_notes.xml
+XML_CHAPTER_FILES = notes.xml
+
+BOOK_FILES = book.xml
+
+XML_FILES = \
+ $(BOOK_FILES) $(XML_CHAPTER_FILES) \
+ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
+
+GIF_FILES =
+
+# ----------------------------------------------------
+
+HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+INFO_FILE = ../../info
+EXTRA_FILES = \
+ $(DEFAULT_GIF_FILES) \
+ $(DEFAULT_HTML_FILES) \
+ $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
+
+MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+$(HTMLDIR)/%.gif: %.gif
+ $(INSTALL_DATA) $< $@
+
+docs: pdf html man
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: gifs $(HTML_REF_MAN_FILE)
+
+man: $(MAN3_FILES)
+
+gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+
+debug opt:
+
+clean clean_docs:
+ rm -rf $(HTMLDIR)/*
+ rm -f $(MAN3DIR)/*
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f errs core *~
+
+distclean: clean
+realclean: clean
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(HTMLDIR)/* \
+ "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
+
+
+release_spec:
diff --git a/lib/typer/doc/src/book.xml b/lib/typer/doc/src/book.xml
new file mode 100644
index 0000000000..5cc85a3022
--- /dev/null
+++ b/lib/typer/doc/src/book.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <copyright>
+ <year>2006</year><year>2013</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>TypEr</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <pagetext></pagetext>
+ <preamble>
+ </preamble>
+ <pagetext>TypEr</pagetext>
+ <applications>
+ <xi:include href="ref_man.xml"/>
+ </applications>
+ <releasenotes>
+ <xi:include href="notes.xml"/>
+ </releasenotes>
+</book>
+
diff --git a/lib/typer/doc/src/fascicules.xml b/lib/typer/doc/src/fascicules.xml
new file mode 100644
index 0000000000..b15610fa8b
--- /dev/null
+++ b/lib/typer/doc/src/fascicules.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
+
+<fascicules>
+ <fascicule file="part_notes" href="part_notes_frame.html" entry="yes">
+ Release Notes
+ </fascicule>
+ <fascicule file="" href="../../../../doc/print.html" entry="no">
+ Off-Print
+ </fascicule>
+</fascicules>
+
diff --git a/lib/typer/doc/src/notes.xml b/lib/typer/doc/src/notes.xml
new file mode 100644
index 0000000000..53d554820d
--- /dev/null
+++ b/lib/typer/doc/src/notes.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2014</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>TypEr Release Notes</title>
+ <prepared>otp_appnotes</prepared>
+ <docno>nil</docno>
+ <date>nil</date>
+ <rev>nil</rev>
+ <file>notes.xml</file>
+ </header>
+ <p>This document describes the changes made to TypEr.</p>
+
+<section><title>TypEr 0.9.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Added initial documentation framework for TypEr.</p>
+ <p>
+ Own Id: OTP-11860</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+
+
+</chapter>
+
diff --git a/lib/typer/doc/src/part_notes.xml b/lib/typer/doc/src/part_notes.xml
new file mode 100644
index 0000000000..b4ccd3ed77
--- /dev/null
+++ b/lib/typer/doc/src/part_notes.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2006</year><year>2013</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>TypEr Release Notes</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <description>
+ <p><em>TypEr</em></p>
+ </description>
+ <xi:include href="notes.xml"/>
+</part>
+
diff --git a/lib/typer/doc/src/ref_man.xml b/lib/typer/doc/src/ref_man.xml
new file mode 100644
index 0000000000..b54a5f5947
--- /dev/null
+++ b/lib/typer/doc/src/ref_man.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2014</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>TypEr</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>ref_man.xml</file>
+ </header>
+ <description>
+ </description>
+ <xi:include href="typer_app.xml"/>
+</application>
+
diff --git a/lib/typer/doc/src/typer_app.xml b/lib/typer/doc/src/typer_app.xml
new file mode 100644
index 0000000000..469a9be108
--- /dev/null
+++ b/lib/typer/doc/src/typer_app.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE appref SYSTEM "appref.dtd">
+
+<appref>
+ <header>
+ <copyright>
+ <year>2014</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>TypEr</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
+ <file>typer.xml</file>
+ </header>
+ <app>TypEr</app>
+ <appsummary>The TypEr Application</appsummary>
+ <description>
+ <p>An Erlang/OTP application that shows type information
+ for Erlang modules to the user. Additionally, it can
+ annotate the code of files with such type information.</p>
+ </description>
+
+</appref>
+
diff --git a/lib/typer/info b/lib/typer/info
new file mode 100644
index 0000000000..5145fbcfff
--- /dev/null
+++ b/lib/typer/info
@@ -0,0 +1,2 @@
+group: tools
+short: TypEr
diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile
index 13af466755..a7059de971 100644
--- a/lib/typer/src/Makefile
+++ b/lib/typer/src/Makefile
@@ -63,7 +63,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += +warn_exported_vars +warn_untyped_record +warn_missing_spec
+ERL_COMPILE_FLAGS += +warn_export_vars +warn_untyped_record +warn_missing_spec
# ----------------------------------------------------
# Targets
diff --git a/lib/typer/src/typer.app.src b/lib/typer/src/typer.app.src
index 850829e1dc..974091b44c 100644
--- a/lib/typer/src/typer.app.src
+++ b/lib/typer/src/typer.app.src
@@ -6,4 +6,6 @@
{modules, [typer]},
{registered, []},
{applications, [compiler, dialyzer, hipe, kernel, stdlib]},
- {env, []}]}.
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","hipe-3.10.3","erts-6.0",
+ "dialyzer-2.7","compiler-5.0"]}]}.
diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk
index 5ac145d9ff..9cc044c621 100644
--- a/lib/typer/vsn.mk
+++ b/lib/typer/vsn.mk
@@ -1 +1 @@
-TYPER_VSN = 0.9.5
+TYPER_VSN = 0.9.7
diff --git a/lib/webtool/doc/src/notes.xml b/lib/webtool/doc/src/notes.xml
index 74e5fd88c1..e571668c91 100644
--- a/lib/webtool/doc/src/notes.xml
+++ b/lib/webtool/doc/src/notes.xml
@@ -31,6 +31,35 @@
<p>This document describes the changes made to the Webtool
application.</p>
+<section><title>WebTool 0.8.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>WebTool 0.8.9.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/webtool/src/webtool.app.src b/lib/webtool/src/webtool.app.src
index 8c6774c533..3d8d11ea60 100644
--- a/lib/webtool/src/webtool.app.src
+++ b/lib/webtool/src/webtool.app.src
@@ -21,5 +21,7 @@
{vsn,"%VSN%"},
{modules,[webtool,webtool_sup]},
{registered,[web_tool,websup]},
- {applications,[kernel,stdlib]}]}.
+ {applications,[kernel,stdlib]},
+ {runtime_dependencies, ["stdlib-2.0","observer-2.0","kernel-3.0",
+ "inets-5.10","erts-6.0"]}]}.
diff --git a/lib/webtool/vsn.mk b/lib/webtool/vsn.mk
index d356a8954d..a79c273d9f 100644
--- a/lib/webtool/vsn.mk
+++ b/lib/webtool/vsn.mk
@@ -1 +1 @@
-WEBTOOL_VSN=0.8.9.2
+WEBTOOL_VSN=0.8.10
diff --git a/lib/wx/aclocal.m4 b/lib/wx/aclocal.m4
index c51c26794a..ed492d55ff 100644
--- a/lib/wx/aclocal.m4
+++ b/lib/wx/aclocal.m4
@@ -83,8 +83,10 @@ AC_ARG_VAR(erl_xcomp_ose_LM_POST_LINK, [OSE postlink tool (only used when cross
AC_ARG_VAR(erl_xcomp_ose_LM_SET_CONF, [Sets the configuration for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_ELF_SIZE, [Prints the section size information for an OSE load module (only used when cross compiling for OSE)])
AC_ARG_VAR(erl_xcomp_ose_LM_LCF, [OSE load module linker configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_LM_CONF, [OSE load module default configuration file (only used when cross compiling for OSE)])
-AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE OSE confd source file])
+AC_ARG_VAR(erl_xcomp_ose_BEAM_LM_CONF, [BEAM OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_EPMD_LM_CONF, [EPMD OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_RUN_ERL_LM_CONF, [run_erl_lm OSE load module default configuration file (only used when cross compiling for OSE)])
+AC_ARG_VAR(erl_xcomp_ose_CONFD, [OSE confd source file])
AC_ARG_VAR(erl_xcomp_ose_CRT0_LM, [OSE crt0 lm source file])
])
@@ -1116,7 +1118,7 @@ case "$THR_LIB_NAME" in
[Define if you have the "ose_spi/ose_spi.h" header file.]))
;;
esac
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
case $host_os in
openbsd*)
# The default stack size is insufficient for our needs
@@ -1220,7 +1222,7 @@ case "$THR_LIB_NAME" in
dnl
dnl Check for functions
dnl
- if test "x$THR_LIB_NAME" == "xpthread"; then
+ if test "x$THR_LIB_NAME" = "xpthread"; then
AC_CHECK_FUNC(pthread_spin_lock, \
[ethr_have_native_spinlock=yes \
AC_DEFINE(ETHR_HAVE_PTHREAD_SPIN_LOCK, 1, \
diff --git a/lib/wx/api_gen/wx_doxygen.conf b/lib/wx/api_gen/wx_doxygen.conf
index 829702cbbf..a8516aa08e 100644
--- a/lib/wx/api_gen/wx_doxygen.conf
+++ b/lib/wx/api_gen/wx_doxygen.conf
@@ -57,7 +57,6 @@ GENERATE_DEPRECATEDLIST= NO
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
-SHOW_DIRECTORIES = YES
FILE_VERSION_FILTER =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
@@ -110,7 +109,6 @@ HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
-HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
@@ -252,6 +250,7 @@ PREDEFINED = \
wxUSE_SLIDER=1 \
wxUSE_CLIPBOARD=1 \
wxUSE_SYSTEM_OPTIONS=1 \
+ wxUSE_INTL=1 \
wxABI_VERSION=20809 \
__WXGTK24__=1 \
__WXGTK20__=1 \
diff --git a/lib/wx/api_gen/wx_extra/wxListCtrl.c_src b/lib/wx/api_gen/wx_extra/wxListCtrl.c_src
index 8fa31e512e..d6196d11a2 100644
--- a/lib/wx/api_gen/wx_extra/wxListCtrl.c_src
+++ b/lib/wx/api_gen/wx_extra/wxListCtrl.c_src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -165,7 +165,7 @@ case ~s: { // wxListCtrl::SortItems taylormade
callbackInfo* cb = new callbackInfo();
cb->port = Ecmd.port;
cb->callbackID = sortCallback;
- bool Result = This->SortItems(wxEListCtrlCompare, (long)cb);
+ bool Result = This->SortItems(wxEListCtrlCompare, (wxeIntPtr)cb);
delete cb;
/* Destroy the callback, see wxEPrintout::clear_cb */
diff --git a/lib/wx/api_gen/wx_gen.erl b/lib/wx/api_gen/wx_gen.erl
index 0f28b3dd5e..a60a9a93d5 100644
--- a/lib/wx/api_gen/wx_gen.erl
+++ b/lib/wx/api_gen/wx_gen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -743,7 +743,14 @@ parse_type2([N="wxTreeItemData"|R],Info,Opts,T) ->
parse_type2(R,Info,Opts,T#type{name="wxETreeItemData",base={term,N}});
parse_type2([N="wxClientData"|R],Info,Opts,T) ->
parse_type2(R,Info,Opts,T#type{name="wxeErlTerm",base={term,N}});
-parse_type2([N="wxChar"|R],Info,Opts,T) ->
+parse_type2([N="wxChar",{by_ref,_}|R],Info,Opts,T = #type{mod=[const]}) ->
+ case get(current_class) of
+ "wxLocale" -> %% Special since changed between 2.8 and 3.0
+ parse_type2(R,Info,Opts,T#type{name="wxeLocaleC",base=string});
+ _ ->
+ parse_type2(R,Info,Opts,T#type{name=N,base=int,single=false})
+ end;
+parse_type2([N="wxChar"|R],Info,Opts,T) ->
parse_type2(R,Info,Opts,T#type{name=N,base=int});
parse_type2(["wxUint32"|R],Info,Opts,T=#type{mod=Mod}) ->
parse_type2(R,Info,Opts,T#type{name=int,base=int,mod=[unsigned|Mod]});
@@ -1274,6 +1281,7 @@ parse_enums([File|Files], Parsed) ->
%%io:format("Parse Enums in ~s ~n", [FileName]),
case xmerl_scan:file(FileName, [{space, normalize}]) of
{error, enoent} ->
+ %% io:format("Ignore ~p~n", [FileName]),
parse_enums(Files, gb_sets:add(File,Parsed));
{Doc, _} ->
ES = "./compounddef/sectiondef/memberdef[@kind=\"enum\"]",
diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl
index ea5d89be72..31ed1374c2 100644
--- a/lib/wx/api_gen/wx_gen_cpp.erl
+++ b/lib/wx/api_gen/wx_gen_cpp.erl
@@ -235,24 +235,27 @@ gen_funcs(Defs) ->
"wxFileDataObject", "wxTextDataObject", "wxBitmapDataObject"
],
- w("void WxeApp::delete_object(void *ptr, wxeRefData *refd) {~n", []),
+ w("bool WxeApp::delete_object(void *ptr, wxeRefData *refd) {~n", []),
w(" switch(refd->type) {~n", []),
- Case = fun(#class{name=Class, id=Id, abstract=IsAbs, parent=P}) when P /= "static" ->
+ Case = fun(C=#class{name=Class, id=Id, abstract=IsAbs, parent=P}) when P /= "static" ->
UglyWorkaround = lists:member(Class, UglySkipList),
+ HaveVirtual = virtual_dest(C),
case hd(reverse(wx_gen_erl:parents(Class))) of
- root when IsAbs == false, UglyWorkaround == false ->
- w(" case ~p: delete (~s *) ptr; break;~n", [Id, Class]);
root when IsAbs == false, UglyWorkaround == true ->
w(" case ~p: /* delete (~s *) ptr;"
"These objects must be deleted by owner object */ "
"break;~n", [Id, Class]);
+ root when IsAbs == false, HaveVirtual == true ->
+ w(" case ~p: delete (E~s *) ptr; return false;~n", [Id, Class]);
+ root when IsAbs == false, UglyWorkaround == false ->
+ w(" case ~p: delete (~s *) ptr; break;~n", [Id, Class]);
_ -> ok
end;
(_) -> ok
end,
[Case(Class) || Class <- Defs],
- w(" default: delete (wxObject *) ptr;~n", []),
- w("}}~n~n", []),
+ w(" default: delete (wxObject *) ptr; return false;~n", []),
+ w(" }~n return true;~n}~n~n", []),
Res.
gen_class(C=#class{name=Name,methods=Ms,options=Opts}) ->
@@ -401,6 +404,8 @@ declare_type(N,true,Def,#type{base=Base,single=true,name=Type,by_val=false,ref={
w(" ~s *~s=~s;~n", [Type,N,Def]);
declare_type(N,true,Def,#type{single=true,name="wxArtClient"}) ->
w(" wxArtClient ~s= ~s;~n", [N,Def]);
+declare_type(N,true,_Def,#type{name="wxeLocaleC", single=true,base=string}) ->
+ w(" wxString ~s= wxEmptyString;~n", [N]);
declare_type(N,true,Def,#type{single=true,base=string}) ->
w(" wxString ~s= ~s;~n", [N,Def]);
%% declare_type(N,true,_Def,#type{name="wxString"}) ->
@@ -997,6 +1002,8 @@ build_ret(Name,_,#type{base=float,single=true}) ->
w(" rt.addFloat(~s);~n",[Name]);
build_ret(Name,_,#type{base=double,single=true}) ->
w(" rt.addFloat(~s);~n",[Name]);
+build_ret(Name,_,#type{name="wxeLocaleC"}) ->
+ w(" rt.add(wxeLocaleC2String(~s));~n",[Name]);
build_ret(Name,_,#type{base=string,single=true}) ->
w(" rt.add(~s);~n",[Name]);
build_ret(Name,_,#type{name="wxArrayString", single=array}) ->
diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl
index a4b03d3fd1..5ac781b40c 100644
--- a/lib/wx/api_gen/wx_gen_erl.erl
+++ b/lib/wx/api_gen/wx_gen_erl.erl
@@ -44,7 +44,11 @@ gen(Defs) ->
gen_unique_names(Defs),
gen_event_recs(),
gen_enums_ints(),
- [gen_class(Class) || Class <- Defs],
+ Static = gen_static([C || C=#class{parent="static"} <- Defs]),
+ Replace = fun(C=#class{name=Name}, Dfs) ->
+ lists:keyreplace(Name, #class.name, Dfs, C)
+ end,
+ [gen_class(Class) || Class <- lists:foldl(Replace, Defs, Static)],
gen_funcnames().
gen_class(Class) ->
@@ -54,9 +58,8 @@ gen_class(Class) ->
Class
end.
-gen_class1(C=#class{name=Name,parent="static",methods=Ms,options=_Opts}) ->
+gen_static(Files) ->
open_write("../src/gen/wx_misc.erl"),
- put(current_class, Name),
erl_copyright(),
w("", []),
w("%% This file is generated DO NOT EDIT~n~n", []),
@@ -67,17 +70,27 @@ gen_class1(C=#class{name=Name,parent="static",methods=Ms,options=_Opts}) ->
w("-module(wx_misc).~n", []),
w("-include(\"wxe.hrl\").~n",[]),
%% w("-compile(export_all).~n~n", []), %% XXXX remove ???
+ [gen_static_exports(C) || C <- Files],
+ Classes = [gen_static_methods(C) || C <- Files],
+ close(),
+ Classes.
+
+gen_static_exports(C=#class{parent="static",methods=Ms}) ->
Exp = fun(M) -> gen_export(C,M) end,
ExportList = lists:usort(lists:append(lists:map(Exp,reverse(Ms)))),
w("-export([~s]).~n~n", [args(fun({EF,_}) -> EF end, ",", ExportList, 60)]),
+ ok.
+gen_static_methods(C=#class{name=Name, parent="static",methods=Ms}) ->
+ put(current_class, Name),
Gen = fun(M) -> gen_method(Name,M) end,
NewMs = lists:map(Gen,reverse(Ms)),
- close(),
erase(current_class),
- C#class{methods=NewMs};
+ C#class{methods=NewMs}.
+gen_class1(C=#class{parent="static"}) ->
+ C;
gen_class1(C=#class{name=Name,parent=Parent,methods=Ms,options=Opts}) ->
case Opts of
["ignore"] -> throw(skipped);
@@ -755,7 +768,7 @@ write_spec(Args, Optional, {complex, Res}, Eol) ->
optional_type(Opts, Eol) ->
"Option :: " ++ args(fun optional_type2/1, Eol++"\t\t | ", Opts).
-optional_type2(#param{name=Name, def=Def, type=T}) ->
+optional_type2(#param{name=Name, def=_Def, type=T}) ->
"{" ++ erl_option_name(Name) ++ ", " ++ doc_arg_type2(T) ++ "}". %% %% Default: " ++ Def.
doc_link("utils", Func) ->
@@ -1357,4 +1370,3 @@ split_list(F, Keep, [M|Ms], Acc) ->
split_list(_, _, [], []) -> [];
split_list(_, _, [], Acc) -> [lists:reverse(Acc)].
-
diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf
index 73c5af43d8..3a1dcc7ba5 100644
--- a/lib/wx/api_gen/wxapi.conf
+++ b/lib/wx/api_gen/wxapi.conf
@@ -1577,9 +1577,14 @@
'wxShutdown', {'wxShell', 1}, 'wxLaunchDefaultBrowser',
{'wxGetEmailAddress',0}, {'wxGetUserId',0}, {'wxGetHomeDir',0},
'wxNewId', 'wxRegisterId', 'wxGetCurrentId',
- 'wxGetOsDescription', 'wxIsPlatformLittleEndian', 'wxIsPlatform64Bit'
+ 'wxGetOsDescription', 'wxIsPlatformLittleEndian', 'wxIsPlatform64Bit'
]}.
+{class, gdicmn, static, [],
+ [ {'wxDisplaySize', [{"width", [out]}, {"height", [out]}]}, 'wxSetCursor'
+ ]}.
+
+
{class, wxPrintout, object, [{alias, [{wxePrintout, wxePrintout}]}],
[{'wxPrintout', [{where, taylormade}]},'~wxPrintout',
'GetDC', %% 'GetPageInfo',Callback
@@ -1885,3 +1890,15 @@
wxEVT_TASKBAR_LEFT_DCLICK,wxEVT_TASKBAR_RIGHT_DCLICK]}],[]}.
{class, wxInitDialogEvent, wxEvent, [{event,[wxEVT_INIT_DIALOG]}], []}.
+
+{class, wxLocale, root, [{skip, [{'wxLocale', 5}, {'Init', 6}]}],
+ ['wxLocale', '~wxLocale', 'Init',
+ 'AddCatalog','AddCatalogLookupPathPrefix',
+ %%'AddLanguage','FindLanguageInfo', 'GetLanguageInfo',
+ 'GetCanonicalName','GetLanguage',
+ 'GetLanguageName','GetLocale','GetName','GetString',
+ 'GetHeaderValue',
+ 'GetSysName',
+ 'GetSystemEncoding','GetSystemEncodingName',
+ 'GetSystemLanguage',
+ 'IsLoaded','IsOk']}.
diff --git a/lib/wx/c_src/gen/wxe_derived_dest.h b/lib/wx/c_src/gen/wxe_derived_dest.h
index 7e2d4524cb..42925bff3a 100644
--- a/lib/wx/c_src/gen/wxe_derived_dest.h
+++ b/lib/wx/c_src/gen/wxe_derived_dest.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -764,3 +764,9 @@ class EwxTaskBarIcon : public wxTaskBarIcon {
EwxTaskBarIcon() : wxTaskBarIcon() {};
};
+class EwxLocale : public wxLocale {
+ public: ~EwxLocale() {((WxeApp *)wxTheApp)->clearPtr(this);};
+ EwxLocale(int language,int flags) : wxLocale(language,flags) {};
+ EwxLocale() : wxLocale() {};
+};
+
diff --git a/lib/wx/c_src/gen/wxe_events.cpp b/lib/wx/c_src/gen/wxe_events.cpp
index 1bd17366a2..0ca059ead4 100644
--- a/lib/wx/c_src/gen/wxe_events.cpp
+++ b/lib/wx/c_src/gen/wxe_events.cpp
@@ -220,84 +220,84 @@ void initEventTable()
{wxEVT_STC_HOTSPOT_DCLICK, 203, "stc_hotspot_dclick"},
{wxEVT_STC_CALLTIP_CLICK, 203, "stc_calltip_click"},
{wxEVT_STC_AUTOCOMP_SELECTION, 203, "stc_autocomp_selection"},
- {wxEVT_COMMAND_TREE_BEGIN_DRAG, 208, "command_tree_begin_drag"},
- {wxEVT_COMMAND_TREE_BEGIN_RDRAG, 208, "command_tree_begin_rdrag"},
- {wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, 208, "command_tree_begin_label_edit"},
- {wxEVT_COMMAND_TREE_END_LABEL_EDIT, 208, "command_tree_end_label_edit"},
- {wxEVT_COMMAND_TREE_DELETE_ITEM, 208, "command_tree_delete_item"},
- {wxEVT_COMMAND_TREE_GET_INFO, 208, "command_tree_get_info"},
- {wxEVT_COMMAND_TREE_SET_INFO, 208, "command_tree_set_info"},
- {wxEVT_COMMAND_TREE_ITEM_EXPANDED, 208, "command_tree_item_expanded"},
- {wxEVT_COMMAND_TREE_ITEM_EXPANDING, 208, "command_tree_item_expanding"},
- {wxEVT_COMMAND_TREE_ITEM_COLLAPSED, 208, "command_tree_item_collapsed"},
- {wxEVT_COMMAND_TREE_ITEM_COLLAPSING, 208, "command_tree_item_collapsing"},
- {wxEVT_COMMAND_TREE_SEL_CHANGED, 208, "command_tree_sel_changed"},
- {wxEVT_COMMAND_TREE_SEL_CHANGING, 208, "command_tree_sel_changing"},
- {wxEVT_COMMAND_TREE_KEY_DOWN, 208, "command_tree_key_down"},
- {wxEVT_COMMAND_TREE_ITEM_ACTIVATED, 208, "command_tree_item_activated"},
- {wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, 208, "command_tree_item_right_click"},
- {wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, 208, "command_tree_item_middle_click"},
- {wxEVT_COMMAND_TREE_END_DRAG, 208, "command_tree_end_drag"},
- {wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, 208, "command_tree_state_image_click"},
- {wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, 208, "command_tree_item_gettooltip"},
- {wxEVT_COMMAND_TREE_ITEM_MENU, 208, "command_tree_item_menu"},
- {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, 209, "command_notebook_page_changed"},
- {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, 209, "command_notebook_page_changing"},
- {wxEVT_COMMAND_TEXT_COPY, 215, "command_text_copy"},
- {wxEVT_COMMAND_TEXT_CUT, 215, "command_text_cut"},
- {wxEVT_COMMAND_TEXT_PASTE, 215, "command_text_paste"},
- {wxEVT_COMMAND_SPINCTRL_UPDATED, 216, "command_spinctrl_updated"},
+ {wxEVT_COMMAND_TREE_BEGIN_DRAG, 209, "command_tree_begin_drag"},
+ {wxEVT_COMMAND_TREE_BEGIN_RDRAG, 209, "command_tree_begin_rdrag"},
+ {wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, 209, "command_tree_begin_label_edit"},
+ {wxEVT_COMMAND_TREE_END_LABEL_EDIT, 209, "command_tree_end_label_edit"},
+ {wxEVT_COMMAND_TREE_DELETE_ITEM, 209, "command_tree_delete_item"},
+ {wxEVT_COMMAND_TREE_GET_INFO, 209, "command_tree_get_info"},
+ {wxEVT_COMMAND_TREE_SET_INFO, 209, "command_tree_set_info"},
+ {wxEVT_COMMAND_TREE_ITEM_EXPANDED, 209, "command_tree_item_expanded"},
+ {wxEVT_COMMAND_TREE_ITEM_EXPANDING, 209, "command_tree_item_expanding"},
+ {wxEVT_COMMAND_TREE_ITEM_COLLAPSED, 209, "command_tree_item_collapsed"},
+ {wxEVT_COMMAND_TREE_ITEM_COLLAPSING, 209, "command_tree_item_collapsing"},
+ {wxEVT_COMMAND_TREE_SEL_CHANGED, 209, "command_tree_sel_changed"},
+ {wxEVT_COMMAND_TREE_SEL_CHANGING, 209, "command_tree_sel_changing"},
+ {wxEVT_COMMAND_TREE_KEY_DOWN, 209, "command_tree_key_down"},
+ {wxEVT_COMMAND_TREE_ITEM_ACTIVATED, 209, "command_tree_item_activated"},
+ {wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, 209, "command_tree_item_right_click"},
+ {wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, 209, "command_tree_item_middle_click"},
+ {wxEVT_COMMAND_TREE_END_DRAG, 209, "command_tree_end_drag"},
+ {wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, 209, "command_tree_state_image_click"},
+ {wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, 209, "command_tree_item_gettooltip"},
+ {wxEVT_COMMAND_TREE_ITEM_MENU, 209, "command_tree_item_menu"},
+ {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, 210, "command_notebook_page_changed"},
+ {wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, 210, "command_notebook_page_changing"},
+ {wxEVT_COMMAND_TEXT_COPY, 216, "command_text_copy"},
+ {wxEVT_COMMAND_TEXT_CUT, 216, "command_text_cut"},
+ {wxEVT_COMMAND_TEXT_PASTE, 216, "command_text_paste"},
+ {wxEVT_COMMAND_SPINCTRL_UPDATED, 217, "command_spinctrl_updated"},
{wxEVT_SCROLL_LINEUP + wxEVT_USER_FIRST, 165, "spin_up"},
{wxEVT_SCROLL_LINEDOWN + wxEVT_USER_FIRST, 165, "spin_down"},
{wxEVT_SCROLL_THUMBTRACK + wxEVT_USER_FIRST, 165, "spin"},
- {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED, 218, "command_splitter_sash_pos_changed"},
- {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING, 218, "command_splitter_sash_pos_changing"},
- {wxEVT_COMMAND_SPLITTER_DOUBLECLICKED, 218, "command_splitter_doubleclicked"},
- {wxEVT_COMMAND_SPLITTER_UNSPLIT, 218, "command_splitter_unsplit"},
- {wxEVT_COMMAND_HTML_LINK_CLICKED, 220, "command_html_link_clicked"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, 223, "command_auinotebook_page_close"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, 223, "command_auinotebook_page_changed"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, 223, "command_auinotebook_page_changing"},
- {wxEVT_COMMAND_AUINOTEBOOK_BUTTON, 223, "command_auinotebook_button"},
- {wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, 223, "command_auinotebook_begin_drag"},
- {wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, 223, "command_auinotebook_end_drag"},
- {wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, 223, "command_auinotebook_drag_motion"},
- {wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, 223, "command_auinotebook_allow_dnd"},
+ {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED, 219, "command_splitter_sash_pos_changed"},
+ {wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING, 219, "command_splitter_sash_pos_changing"},
+ {wxEVT_COMMAND_SPLITTER_DOUBLECLICKED, 219, "command_splitter_doubleclicked"},
+ {wxEVT_COMMAND_SPLITTER_UNSPLIT, 219, "command_splitter_unsplit"},
+ {wxEVT_COMMAND_HTML_LINK_CLICKED, 221, "command_html_link_clicked"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, 224, "command_auinotebook_page_close"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, 224, "command_auinotebook_page_changed"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, 224, "command_auinotebook_page_changing"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BUTTON, 224, "command_auinotebook_button"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, 224, "command_auinotebook_begin_drag"},
+ {wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, 224, "command_auinotebook_end_drag"},
+ {wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, 224, "command_auinotebook_drag_motion"},
+ {wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, 224, "command_auinotebook_allow_dnd"},
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, 223, "command_auinotebook_tab_middle_down"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, 224, "command_auinotebook_tab_middle_down"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, 223, "command_auinotebook_tab_middle_up"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, 224, "command_auinotebook_tab_middle_up"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, 223, "command_auinotebook_tab_right_down"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, 224, "command_auinotebook_tab_right_down"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, 223, "command_auinotebook_tab_right_up"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, 224, "command_auinotebook_tab_right_up"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, 223, "command_auinotebook_page_closed"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, 224, "command_auinotebook_page_closed"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, 223, "command_auinotebook_drag_done"},
+ {wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, 224, "command_auinotebook_drag_done"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, 223, "command_auinotebook_bg_dclick"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, 224, "command_auinotebook_bg_dclick"},
#endif
- {wxEVT_AUI_PANE_BUTTON, 224, "aui_pane_button"},
- {wxEVT_AUI_PANE_CLOSE, 224, "aui_pane_close"},
- {wxEVT_AUI_PANE_MAXIMIZE, 224, "aui_pane_maximize"},
- {wxEVT_AUI_PANE_RESTORE, 224, "aui_pane_restore"},
- {wxEVT_AUI_RENDER, 224, "aui_render"},
- {wxEVT_AUI_FIND_MANAGER, 224, "aui_find_manager"},
- {wxEVT_TASKBAR_MOVE, 227, "taskbar_move"},
- {wxEVT_TASKBAR_LEFT_DOWN, 227, "taskbar_left_down"},
- {wxEVT_TASKBAR_LEFT_UP, 227, "taskbar_left_up"},
- {wxEVT_TASKBAR_RIGHT_DOWN, 227, "taskbar_right_down"},
- {wxEVT_TASKBAR_RIGHT_UP, 227, "taskbar_right_up"},
- {wxEVT_TASKBAR_LEFT_DCLICK, 227, "taskbar_left_dclick"},
- {wxEVT_TASKBAR_RIGHT_DCLICK, 227, "taskbar_right_dclick"},
- {wxEVT_INIT_DIALOG, 228, "init_dialog"},
+ {wxEVT_AUI_PANE_BUTTON, 225, "aui_pane_button"},
+ {wxEVT_AUI_PANE_CLOSE, 225, "aui_pane_close"},
+ {wxEVT_AUI_PANE_MAXIMIZE, 225, "aui_pane_maximize"},
+ {wxEVT_AUI_PANE_RESTORE, 225, "aui_pane_restore"},
+ {wxEVT_AUI_RENDER, 225, "aui_render"},
+ {wxEVT_AUI_FIND_MANAGER, 225, "aui_find_manager"},
+ {wxEVT_TASKBAR_MOVE, 228, "taskbar_move"},
+ {wxEVT_TASKBAR_LEFT_DOWN, 228, "taskbar_left_down"},
+ {wxEVT_TASKBAR_LEFT_UP, 228, "taskbar_left_up"},
+ {wxEVT_TASKBAR_RIGHT_DOWN, 228, "taskbar_right_down"},
+ {wxEVT_TASKBAR_RIGHT_UP, 228, "taskbar_right_up"},
+ {wxEVT_TASKBAR_LEFT_DCLICK, 228, "taskbar_left_dclick"},
+ {wxEVT_TASKBAR_RIGHT_DCLICK, 228, "taskbar_right_dclick"},
+ {wxEVT_INIT_DIALOG, 229, "init_dialog"},
{-1, 0, }
};
for(int i=0; event_types[i].ev_type != -1; i++) {
@@ -719,7 +719,7 @@ case 203: {// wxStyledTextEvent
rt.addTupleCount(22);
break;
}
-case 208: {// wxTreeEvent
+case 209: {// wxTreeEvent
wxTreeEvent * ev = (wxTreeEvent *) event;
evClass = (char*)"wxTreeEvent";
rt.addAtom((char*)"wxTree");
@@ -730,21 +730,21 @@ case 208: {// wxTreeEvent
rt.addTupleCount(5);
break;
}
-case 209: {// wxNotebookEvent
+case 210: {// wxNotebookEvent
evClass = (char*)"wxNotebookEvent";
rt.addAtom((char*)"wxNotebook");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 215: {// wxClipboardTextEvent
+case 216: {// wxClipboardTextEvent
evClass = (char*)"wxClipboardTextEvent";
rt.addAtom((char*)"wxClipboardText");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 216: {// wxSpinEvent
+case 217: {// wxSpinEvent
wxSpinEvent * ev = (wxSpinEvent *) event;
evClass = (char*)"wxSpinEvent";
rt.addAtom((char*)"wxSpin");
@@ -753,14 +753,14 @@ case 216: {// wxSpinEvent
rt.addTupleCount(3);
break;
}
-case 218: {// wxSplitterEvent
+case 219: {// wxSplitterEvent
evClass = (char*)"wxSplitterEvent";
rt.addAtom((char*)"wxSplitter");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 220: {// wxHtmlLinkEvent
+case 221: {// wxHtmlLinkEvent
wxHtmlLinkEvent * ev = (wxHtmlLinkEvent *) event;
evClass = (char*)"wxHtmlLinkEvent";
rt.addAtom((char*)"wxHtmlLink");
@@ -769,7 +769,7 @@ case 220: {// wxHtmlLinkEvent
rt.addTupleCount(3);
break;
}
-case 223: {// wxAuiNotebookEvent
+case 224: {// wxAuiNotebookEvent
wxAuiNotebookEvent * ev = (wxAuiNotebookEvent *) event;
wxAuiNotebook * GetDragSource = ev->GetDragSource();
evClass = (char*)"wxAuiNotebookEvent";
@@ -781,7 +781,7 @@ case 223: {// wxAuiNotebookEvent
rt.addTupleCount(5);
break;
}
-case 224: {// wxAuiManagerEvent
+case 225: {// wxAuiManagerEvent
wxAuiManagerEvent * ev = (wxAuiManagerEvent *) event;
wxAuiManager * GetManager = ev->GetManager();
wxAuiPaneInfo * GetPane = ev->GetPane();
@@ -798,14 +798,14 @@ case 224: {// wxAuiManagerEvent
rt.addTupleCount(8);
break;
}
-case 227: {// wxTaskBarIconEvent
+case 228: {// wxTaskBarIconEvent
evClass = (char*)"wxTaskBarIconEvent";
rt.addAtom((char*)"wxTaskBarIcon");
rt.addAtom(Etype->eName);
rt.addTupleCount(2);
break;
}
-case 228: {// wxInitDialogEvent
+case 229: {// wxInitDialogEvent
evClass = (char*)"wxInitDialogEvent";
rt.addAtom((char*)"wxInitDialog");
rt.addAtom(Etype->eName);
diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp
index 3f5cb4c0f5..c1e9f3829a 100644
--- a/lib/wx/c_src/gen/wxe_funcs.cpp
+++ b/lib/wx/c_src/gen/wxe_funcs.cpp
@@ -15875,7 +15875,7 @@ case wxListCtrl_SortItems: { // wxListCtrl::SortItems taylormade
callbackInfo* cb = new callbackInfo();
cb->port = Ecmd.port;
cb->callbackID = sortCallback;
- bool Result = This->SortItems(wxEListCtrlCompare, (long)cb);
+ bool Result = This->SortItems(wxEListCtrlCompare, (wxeIntPtr)cb);
delete cb;
/* Destroy the callback, see wxEPrintout::clear_cb */
@@ -26730,6 +26730,20 @@ case utils_wxIsPlatform64Bit: { // utils::wxIsPlatform64Bit
rt.addBool(Result);
break;
}
+case gdicmn_wxDisplaySize: { // gdicmn::wxDisplaySize
+ int width;
+ int height;
+ ::wxDisplaySize(&width,&height);
+ rt.addInt(width);
+ rt.addInt(height);
+ rt.addTupleCount(2);
+ break;
+}
+case gdicmn_wxSetCursor: { // gdicmn::wxSetCursor
+ wxCursor *cursor = (wxCursor *) getPtr(bp,memenv); bp += 4;
+ ::wxSetCursor(*cursor);
+ break;
+}
case wxPrintout_new: { // wxPrintout::wxPrintout taylormade
int onPreparePrinting=0,onBeginPrinting=0,onEndPrinting=0,onBeginDocument=0,
@@ -30442,7 +30456,7 @@ case wxNotebookEvent_SetSelection: { // wxNotebookEvent::SetSelection
}
case wxFileDataObject_new: { // wxFileDataObject::wxFileDataObject
wxFileDataObject * Result = new wxFileDataObject();
- newPtr((void *) Result, 211, memenv);
+ newPtr((void *) Result, 212, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxFileDataObject");
break;
}
@@ -30478,7 +30492,7 @@ case wxTextDataObject_new: { // wxTextDataObject::wxTextDataObject
} break;
}};
wxTextDataObject * Result = new wxTextDataObject(text);
- newPtr((void *) Result, 212, memenv);
+ newPtr((void *) Result, 213, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxTextDataObject");
break;
}
@@ -30514,7 +30528,7 @@ case wxTextDataObject_destroy: { // wxTextDataObject::destroy
case wxBitmapDataObject_new_1_1: { // wxBitmapDataObject::wxBitmapDataObject
wxBitmap *bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
wxBitmapDataObject * Result = new wxBitmapDataObject(*bitmap);
- newPtr((void *) Result, 213, memenv);
+ newPtr((void *) Result, 214, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxBitmapDataObject");
break;
}
@@ -30526,7 +30540,7 @@ bitmap = (wxBitmap *) getPtr(bp,memenv); bp += 4;
} break;
}};
wxBitmapDataObject * Result = new wxBitmapDataObject(*bitmap);
- newPtr((void *) Result, 213, memenv);
+ newPtr((void *) Result, 214, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxBitmapDataObject");
break;
}
@@ -31359,7 +31373,7 @@ case wxAuiManagerEvent_CanVeto: { // wxAuiManagerEvent::CanVeto
}
case wxLogNull_new: { // wxLogNull::wxLogNull
wxLogNull * Result = new wxLogNull();
- newPtr((void *) Result, 225, memenv);
+ newPtr((void *) Result, 226, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxLogNull");
break;
}
@@ -31406,6 +31420,213 @@ case wxTaskBarIcon_SetIcon: { // wxTaskBarIcon::SetIcon
rt.addBool(Result);
break;
}
+case wxLocale_new_0: { // wxLocale::wxLocale
+ wxLocale * Result = new EwxLocale();
+ newPtr((void *) Result, 230, memenv);
+ rt.addRef(getRef((void *)Result,memenv), "wxLocale");
+ break;
+}
+case wxLocale_new_2: { // wxLocale::wxLocale
+ int flags=wxLOCALE_LOAD_DEFAULT|wxLOCALE_CONV_ENCODING;
+ int * language = (int *) bp; bp += 4;
+ bp += 4; /* Align */
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+ flags = (int)*(int *) bp; bp += 4;
+ } break;
+ }};
+ wxLocale * Result = new EwxLocale(*language,flags);
+ newPtr((void *) Result, 230, memenv);
+ rt.addRef(getRef((void *)Result,memenv), "wxLocale");
+ break;
+}
+case wxLocale_destruct: { // wxLocale::~wxLocale
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(This) { ((WxeApp *) wxTheApp)->clearPtr((void *) This);
+ delete This;}
+ break;
+}
+case wxLocale_Init: { // wxLocale::Init
+ int language=wxLANGUAGE_DEFAULT;
+ int flags=wxLOCALE_LOAD_DEFAULT|wxLOCALE_CONV_ENCODING;
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ bp += 4; /* Align */
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+ language = (int)*(int *) bp; bp += 4;
+ } break;
+ case 2: {bp += 4;
+ flags = (int)*(int *) bp; bp += 4;
+ } break;
+ }};
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->Init(language,flags);
+ rt.addBool(Result);
+ break;
+}
+case wxLocale_AddCatalog_1: { // wxLocale::AddCatalog
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ wxString szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->AddCatalog(szDomain);
+ rt.addBool(Result);
+ break;
+}
+case wxLocale_AddCatalog_3: { // wxLocale::AddCatalog
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ wxString szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ wxLanguage msgIdLanguage = *(wxLanguage *) bp; bp += 4;;
+ int * msgIdCharsetLen = (int *) bp; bp += 4;
+ wxString msgIdCharset = wxString(bp, wxConvUTF8);
+ bp += *msgIdCharsetLen+((8-((0+ *msgIdCharsetLen) & 7)) & 7);
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->AddCatalog(szDomain,msgIdLanguage,msgIdCharset);
+ rt.addBool(Result);
+ break;
+}
+case wxLocale_AddCatalogLookupPathPrefix: { // wxLocale::AddCatalogLookupPathPrefix
+ int * prefixLen = (int *) bp; bp += 4;
+ wxString prefix = wxString(bp, wxConvUTF8);
+ bp += *prefixLen+((8-((4+ *prefixLen) & 7)) & 7);
+ wxLocale::AddCatalogLookupPathPrefix(prefix);
+ break;
+}
+case wxLocale_GetCanonicalName: { // wxLocale::GetCanonicalName
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ wxString Result = This->GetCanonicalName();
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetLanguage: { // wxLocale::GetLanguage
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ int Result = This->GetLanguage();
+ rt.addInt(Result);
+ break;
+}
+case wxLocale_GetLanguageName: { // wxLocale::GetLanguageName
+ int * lang = (int *) bp; bp += 4;
+ wxString Result = wxLocale::GetLanguageName(*lang);
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetLocale: { // wxLocale::GetLocale
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ const wxeLocaleC Result = This->GetLocale();
+ rt.add(wxeLocaleC2String(Result));
+ break;
+}
+case wxLocale_GetName: { // wxLocale::GetName
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ const wxString * Result = &This->GetName();
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetString_2: { // wxLocale::GetString
+ wxString szDomain= wxEmptyString;
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szOrigStringLen = (int *) bp; bp += 4;
+ wxString szOrigString = wxString(bp, wxConvUTF8);
+ bp += *szOrigStringLen+((8-((0+ *szOrigStringLen) & 7)) & 7);
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ } break;
+ }};
+ if(!This) throw wxe_badarg(0);
+ const wxeLocaleC Result = This->GetString(szOrigString,szDomain);
+ rt.add(wxeLocaleC2String(Result));
+ break;
+}
+case wxLocale_GetString_4: { // wxLocale::GetString
+ wxString szDomain= wxEmptyString;
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szOrigStringLen = (int *) bp; bp += 4;
+ wxString szOrigString = wxString(bp, wxConvUTF8);
+ bp += *szOrigStringLen+((8-((0+ *szOrigStringLen) & 7)) & 7);
+ int * szOrigString2Len = (int *) bp; bp += 4;
+ wxString szOrigString2 = wxString(bp, wxConvUTF8);
+ bp += *szOrigString2Len+((8-((4+ *szOrigString2Len) & 7)) & 7);
+ int * n = (int *) bp; bp += 4;
+ bp += 4; /* Align */
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ } break;
+ }};
+ if(!This) throw wxe_badarg(0);
+ const wxeLocaleC Result = This->GetString(szOrigString,szOrigString2,*n,szDomain);
+ rt.add(wxeLocaleC2String(Result));
+ break;
+}
+case wxLocale_GetHeaderValue: { // wxLocale::GetHeaderValue
+ wxString szDomain= wxEmptyString;
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szHeaderLen = (int *) bp; bp += 4;
+ wxString szHeader = wxString(bp, wxConvUTF8);
+ bp += *szHeaderLen+((8-((0+ *szHeaderLen) & 7)) & 7);
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ } break;
+ }};
+ if(!This) throw wxe_badarg(0);
+ wxString Result = This->GetHeaderValue(szHeader,szDomain);
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetSysName: { // wxLocale::GetSysName
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ wxString Result = This->GetSysName();
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetSystemEncoding: { // wxLocale::GetSystemEncoding
+ int Result = wxLocale::GetSystemEncoding();
+ rt.addInt(Result);
+ break;
+}
+case wxLocale_GetSystemEncodingName: { // wxLocale::GetSystemEncodingName
+ wxString Result = wxLocale::GetSystemEncodingName();
+ rt.add(Result);
+ break;
+}
+case wxLocale_GetSystemLanguage: { // wxLocale::GetSystemLanguage
+ int Result = wxLocale::GetSystemLanguage();
+ rt.addInt(Result);
+ break;
+}
+case wxLocale_IsLoaded: { // wxLocale::IsLoaded
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ int * szDomainLen = (int *) bp; bp += 4;
+ wxString szDomain = wxString(bp, wxConvUTF8);
+ bp += *szDomainLen+((8-((0+ *szDomainLen) & 7)) & 7);
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->IsLoaded(szDomain);
+ rt.addBool(Result);
+ break;
+}
+case wxLocale_IsOk: { // wxLocale::IsOk
+ wxLocale *This = (wxLocale *) getPtr(bp,memenv); bp += 4;
+ if(!This) throw wxe_badarg(0);
+ bool Result = This->IsOk();
+ rt.addBool(Result);
+ break;
+}
default: {
wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false); error.addAtom("_wxe_error_");
error.addInt((int) Ecmd.op);
@@ -31427,7 +31648,7 @@ case wxTaskBarIcon_SetIcon: { // wxTaskBarIcon::SetIcon
}} /* The End */
-void WxeApp::delete_object(void *ptr, wxeRefData *refd) {
+bool WxeApp::delete_object(void *ptr, wxeRefData *refd) {
switch(refd->type) {
case 24: delete (wxGridCellBoolRenderer *) ptr; break;
case 25: delete (wxGridCellBoolEditor *) ptr; break;
@@ -31446,10 +31667,13 @@ void WxeApp::delete_object(void *ptr, wxeRefData *refd) {
case 101: delete (wxListItemAttr *) ptr; break;
case 103: delete (wxTextAttr *) ptr; break;
case 155: delete (wxAuiPaneInfo *) ptr; break;
- case 211: /* delete (wxFileDataObject *) ptr;These objects must be deleted by owner object */ break;
- case 212: /* delete (wxTextDataObject *) ptr;These objects must be deleted by owner object */ break;
- case 213: /* delete (wxBitmapDataObject *) ptr;These objects must be deleted by owner object */ break;
- case 225: delete (wxLogNull *) ptr; break;
- default: delete (wxObject *) ptr;
-}}
+ case 212: /* delete (wxFileDataObject *) ptr;These objects must be deleted by owner object */ break;
+ case 213: /* delete (wxTextDataObject *) ptr;These objects must be deleted by owner object */ break;
+ case 214: /* delete (wxBitmapDataObject *) ptr;These objects must be deleted by owner object */ break;
+ case 226: delete (wxLogNull *) ptr; break;
+ case 230: delete (EwxLocale *) ptr; return false;
+ default: delete (wxObject *) ptr; return false;
+ }
+ return true;
+}
diff --git a/lib/wx/c_src/gen/wxe_macros.h b/lib/wx/c_src/gen/wxe_macros.h
index a1da6500d5..2da24f5d5e 100644
--- a/lib/wx/c_src/gen/wxe_macros.h
+++ b/lib/wx/c_src/gen/wxe_macros.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2014. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -2756,601 +2756,624 @@
#define utils_wxGetOsDescription 2924
#define utils_wxIsPlatformLittleEndian 2925
#define utils_wxIsPlatform64Bit 2926
-#define wxPrintout_new 2927
-#define wxPrintout_destruct 2928
-#define wxPrintout_GetDC 2929
-#define wxPrintout_GetPageSizeMM 2930
-#define wxPrintout_GetPageSizePixels 2931
-#define wxPrintout_GetPaperRectPixels 2932
-#define wxPrintout_GetPPIPrinter 2933
-#define wxPrintout_GetPPIScreen 2934
-#define wxPrintout_GetTitle 2935
-#define wxPrintout_IsPreview 2936
-#define wxPrintout_FitThisSizeToPaper 2937
-#define wxPrintout_FitThisSizeToPage 2938
-#define wxPrintout_FitThisSizeToPageMargins 2939
-#define wxPrintout_MapScreenSizeToPaper 2940
-#define wxPrintout_MapScreenSizeToPage 2941
-#define wxPrintout_MapScreenSizeToPageMargins 2942
-#define wxPrintout_MapScreenSizeToDevice 2943
-#define wxPrintout_GetLogicalPaperRect 2944
-#define wxPrintout_GetLogicalPageRect 2945
-#define wxPrintout_GetLogicalPageMarginsRect 2946
-#define wxPrintout_SetLogicalOrigin 2947
-#define wxPrintout_OffsetLogicalOrigin 2948
-#define wxStyledTextCtrl_new_2 2949
-#define wxStyledTextCtrl_new_0 2950
-#define wxStyledTextCtrl_destruct 2951
-#define wxStyledTextCtrl_Create 2952
-#define wxStyledTextCtrl_AddText 2953
-#define wxStyledTextCtrl_AddStyledText 2954
-#define wxStyledTextCtrl_InsertText 2955
-#define wxStyledTextCtrl_ClearAll 2956
-#define wxStyledTextCtrl_ClearDocumentStyle 2957
-#define wxStyledTextCtrl_GetLength 2958
-#define wxStyledTextCtrl_GetCharAt 2959
-#define wxStyledTextCtrl_GetCurrentPos 2960
-#define wxStyledTextCtrl_GetAnchor 2961
-#define wxStyledTextCtrl_GetStyleAt 2962
-#define wxStyledTextCtrl_Redo 2963
-#define wxStyledTextCtrl_SetUndoCollection 2964
-#define wxStyledTextCtrl_SelectAll 2965
-#define wxStyledTextCtrl_SetSavePoint 2966
-#define wxStyledTextCtrl_GetStyledText 2967
-#define wxStyledTextCtrl_CanRedo 2968
-#define wxStyledTextCtrl_MarkerLineFromHandle 2969
-#define wxStyledTextCtrl_MarkerDeleteHandle 2970
-#define wxStyledTextCtrl_GetUndoCollection 2971
-#define wxStyledTextCtrl_GetViewWhiteSpace 2972
-#define wxStyledTextCtrl_SetViewWhiteSpace 2973
-#define wxStyledTextCtrl_PositionFromPoint 2974
-#define wxStyledTextCtrl_PositionFromPointClose 2975
-#define wxStyledTextCtrl_GotoLine 2976
-#define wxStyledTextCtrl_GotoPos 2977
-#define wxStyledTextCtrl_SetAnchor 2978
-#define wxStyledTextCtrl_GetCurLine 2979
-#define wxStyledTextCtrl_GetEndStyled 2980
-#define wxStyledTextCtrl_ConvertEOLs 2981
-#define wxStyledTextCtrl_GetEOLMode 2982
-#define wxStyledTextCtrl_SetEOLMode 2983
-#define wxStyledTextCtrl_StartStyling 2984
-#define wxStyledTextCtrl_SetStyling 2985
-#define wxStyledTextCtrl_GetBufferedDraw 2986
-#define wxStyledTextCtrl_SetBufferedDraw 2987
-#define wxStyledTextCtrl_SetTabWidth 2988
-#define wxStyledTextCtrl_GetTabWidth 2989
-#define wxStyledTextCtrl_SetCodePage 2990
-#define wxStyledTextCtrl_MarkerDefine 2991
-#define wxStyledTextCtrl_MarkerSetForeground 2992
-#define wxStyledTextCtrl_MarkerSetBackground 2993
-#define wxStyledTextCtrl_MarkerAdd 2994
-#define wxStyledTextCtrl_MarkerDelete 2995
-#define wxStyledTextCtrl_MarkerDeleteAll 2996
-#define wxStyledTextCtrl_MarkerGet 2997
-#define wxStyledTextCtrl_MarkerNext 2998
-#define wxStyledTextCtrl_MarkerPrevious 2999
-#define wxStyledTextCtrl_MarkerDefineBitmap 3000
-#define wxStyledTextCtrl_MarkerAddSet 3001
-#define wxStyledTextCtrl_MarkerSetAlpha 3002
-#define wxStyledTextCtrl_SetMarginType 3003
-#define wxStyledTextCtrl_GetMarginType 3004
-#define wxStyledTextCtrl_SetMarginWidth 3005
-#define wxStyledTextCtrl_GetMarginWidth 3006
-#define wxStyledTextCtrl_SetMarginMask 3007
-#define wxStyledTextCtrl_GetMarginMask 3008
-#define wxStyledTextCtrl_SetMarginSensitive 3009
-#define wxStyledTextCtrl_GetMarginSensitive 3010
-#define wxStyledTextCtrl_StyleClearAll 3011
-#define wxStyledTextCtrl_StyleSetForeground 3012
-#define wxStyledTextCtrl_StyleSetBackground 3013
-#define wxStyledTextCtrl_StyleSetBold 3014
-#define wxStyledTextCtrl_StyleSetItalic 3015
-#define wxStyledTextCtrl_StyleSetSize 3016
-#define wxStyledTextCtrl_StyleSetFaceName 3017
-#define wxStyledTextCtrl_StyleSetEOLFilled 3018
-#define wxStyledTextCtrl_StyleResetDefault 3019
-#define wxStyledTextCtrl_StyleSetUnderline 3020
-#define wxStyledTextCtrl_StyleSetCase 3021
-#define wxStyledTextCtrl_StyleSetHotSpot 3022
-#define wxStyledTextCtrl_SetSelForeground 3023
-#define wxStyledTextCtrl_SetSelBackground 3024
-#define wxStyledTextCtrl_GetSelAlpha 3025
-#define wxStyledTextCtrl_SetSelAlpha 3026
-#define wxStyledTextCtrl_SetCaretForeground 3027
-#define wxStyledTextCtrl_CmdKeyAssign 3028
-#define wxStyledTextCtrl_CmdKeyClear 3029
-#define wxStyledTextCtrl_CmdKeyClearAll 3030
-#define wxStyledTextCtrl_SetStyleBytes 3031
-#define wxStyledTextCtrl_StyleSetVisible 3032
-#define wxStyledTextCtrl_GetCaretPeriod 3033
-#define wxStyledTextCtrl_SetCaretPeriod 3034
-#define wxStyledTextCtrl_SetWordChars 3035
-#define wxStyledTextCtrl_BeginUndoAction 3036
-#define wxStyledTextCtrl_EndUndoAction 3037
-#define wxStyledTextCtrl_IndicatorSetStyle 3038
-#define wxStyledTextCtrl_IndicatorGetStyle 3039
-#define wxStyledTextCtrl_IndicatorSetForeground 3040
-#define wxStyledTextCtrl_IndicatorGetForeground 3041
-#define wxStyledTextCtrl_SetWhitespaceForeground 3042
-#define wxStyledTextCtrl_SetWhitespaceBackground 3043
-#define wxStyledTextCtrl_GetStyleBits 3044
-#define wxStyledTextCtrl_SetLineState 3045
-#define wxStyledTextCtrl_GetLineState 3046
-#define wxStyledTextCtrl_GetMaxLineState 3047
-#define wxStyledTextCtrl_GetCaretLineVisible 3048
-#define wxStyledTextCtrl_SetCaretLineVisible 3049
-#define wxStyledTextCtrl_GetCaretLineBackground 3050
-#define wxStyledTextCtrl_SetCaretLineBackground 3051
-#define wxStyledTextCtrl_AutoCompShow 3052
-#define wxStyledTextCtrl_AutoCompCancel 3053
-#define wxStyledTextCtrl_AutoCompActive 3054
-#define wxStyledTextCtrl_AutoCompPosStart 3055
-#define wxStyledTextCtrl_AutoCompComplete 3056
-#define wxStyledTextCtrl_AutoCompStops 3057
-#define wxStyledTextCtrl_AutoCompSetSeparator 3058
-#define wxStyledTextCtrl_AutoCompGetSeparator 3059
-#define wxStyledTextCtrl_AutoCompSelect 3060
-#define wxStyledTextCtrl_AutoCompSetCancelAtStart 3061
-#define wxStyledTextCtrl_AutoCompGetCancelAtStart 3062
-#define wxStyledTextCtrl_AutoCompSetFillUps 3063
-#define wxStyledTextCtrl_AutoCompSetChooseSingle 3064
-#define wxStyledTextCtrl_AutoCompGetChooseSingle 3065
-#define wxStyledTextCtrl_AutoCompSetIgnoreCase 3066
-#define wxStyledTextCtrl_AutoCompGetIgnoreCase 3067
-#define wxStyledTextCtrl_UserListShow 3068
-#define wxStyledTextCtrl_AutoCompSetAutoHide 3069
-#define wxStyledTextCtrl_AutoCompGetAutoHide 3070
-#define wxStyledTextCtrl_AutoCompSetDropRestOfWord 3071
-#define wxStyledTextCtrl_AutoCompGetDropRestOfWord 3072
-#define wxStyledTextCtrl_RegisterImage 3073
-#define wxStyledTextCtrl_ClearRegisteredImages 3074
-#define wxStyledTextCtrl_AutoCompGetTypeSeparator 3075
-#define wxStyledTextCtrl_AutoCompSetTypeSeparator 3076
-#define wxStyledTextCtrl_AutoCompSetMaxWidth 3077
-#define wxStyledTextCtrl_AutoCompGetMaxWidth 3078
-#define wxStyledTextCtrl_AutoCompSetMaxHeight 3079
-#define wxStyledTextCtrl_AutoCompGetMaxHeight 3080
-#define wxStyledTextCtrl_SetIndent 3081
-#define wxStyledTextCtrl_GetIndent 3082
-#define wxStyledTextCtrl_SetUseTabs 3083
-#define wxStyledTextCtrl_GetUseTabs 3084
-#define wxStyledTextCtrl_SetLineIndentation 3085
-#define wxStyledTextCtrl_GetLineIndentation 3086
-#define wxStyledTextCtrl_GetLineIndentPosition 3087
-#define wxStyledTextCtrl_GetColumn 3088
-#define wxStyledTextCtrl_SetUseHorizontalScrollBar 3089
-#define wxStyledTextCtrl_GetUseHorizontalScrollBar 3090
-#define wxStyledTextCtrl_SetIndentationGuides 3091
-#define wxStyledTextCtrl_GetIndentationGuides 3092
-#define wxStyledTextCtrl_SetHighlightGuide 3093
-#define wxStyledTextCtrl_GetHighlightGuide 3094
-#define wxStyledTextCtrl_GetLineEndPosition 3095
-#define wxStyledTextCtrl_GetCodePage 3096
-#define wxStyledTextCtrl_GetCaretForeground 3097
-#define wxStyledTextCtrl_GetReadOnly 3098
-#define wxStyledTextCtrl_SetCurrentPos 3099
-#define wxStyledTextCtrl_SetSelectionStart 3100
-#define wxStyledTextCtrl_GetSelectionStart 3101
-#define wxStyledTextCtrl_SetSelectionEnd 3102
-#define wxStyledTextCtrl_GetSelectionEnd 3103
-#define wxStyledTextCtrl_SetPrintMagnification 3104
-#define wxStyledTextCtrl_GetPrintMagnification 3105
-#define wxStyledTextCtrl_SetPrintColourMode 3106
-#define wxStyledTextCtrl_GetPrintColourMode 3107
-#define wxStyledTextCtrl_FindText 3108
-#define wxStyledTextCtrl_FormatRange 3109
-#define wxStyledTextCtrl_GetFirstVisibleLine 3110
-#define wxStyledTextCtrl_GetLine 3111
-#define wxStyledTextCtrl_GetLineCount 3112
-#define wxStyledTextCtrl_SetMarginLeft 3113
-#define wxStyledTextCtrl_GetMarginLeft 3114
-#define wxStyledTextCtrl_SetMarginRight 3115
-#define wxStyledTextCtrl_GetMarginRight 3116
-#define wxStyledTextCtrl_GetModify 3117
-#define wxStyledTextCtrl_SetSelection 3118
-#define wxStyledTextCtrl_GetSelectedText 3119
-#define wxStyledTextCtrl_GetTextRange 3120
-#define wxStyledTextCtrl_HideSelection 3121
-#define wxStyledTextCtrl_LineFromPosition 3122
-#define wxStyledTextCtrl_PositionFromLine 3123
-#define wxStyledTextCtrl_LineScroll 3124
-#define wxStyledTextCtrl_EnsureCaretVisible 3125
-#define wxStyledTextCtrl_ReplaceSelection 3126
-#define wxStyledTextCtrl_SetReadOnly 3127
-#define wxStyledTextCtrl_CanPaste 3128
-#define wxStyledTextCtrl_CanUndo 3129
-#define wxStyledTextCtrl_EmptyUndoBuffer 3130
-#define wxStyledTextCtrl_Undo 3131
-#define wxStyledTextCtrl_Cut 3132
-#define wxStyledTextCtrl_Copy 3133
-#define wxStyledTextCtrl_Paste 3134
-#define wxStyledTextCtrl_Clear 3135
-#define wxStyledTextCtrl_SetText 3136
-#define wxStyledTextCtrl_GetText 3137
-#define wxStyledTextCtrl_GetTextLength 3138
-#define wxStyledTextCtrl_GetOvertype 3139
-#define wxStyledTextCtrl_SetCaretWidth 3140
-#define wxStyledTextCtrl_GetCaretWidth 3141
-#define wxStyledTextCtrl_SetTargetStart 3142
-#define wxStyledTextCtrl_GetTargetStart 3143
-#define wxStyledTextCtrl_SetTargetEnd 3144
-#define wxStyledTextCtrl_GetTargetEnd 3145
-#define wxStyledTextCtrl_ReplaceTarget 3146
-#define wxStyledTextCtrl_SearchInTarget 3147
-#define wxStyledTextCtrl_SetSearchFlags 3148
-#define wxStyledTextCtrl_GetSearchFlags 3149
-#define wxStyledTextCtrl_CallTipShow 3150
-#define wxStyledTextCtrl_CallTipCancel 3151
-#define wxStyledTextCtrl_CallTipActive 3152
-#define wxStyledTextCtrl_CallTipPosAtStart 3153
-#define wxStyledTextCtrl_CallTipSetHighlight 3154
-#define wxStyledTextCtrl_CallTipSetBackground 3155
-#define wxStyledTextCtrl_CallTipSetForeground 3156
-#define wxStyledTextCtrl_CallTipSetForegroundHighlight 3157
-#define wxStyledTextCtrl_CallTipUseStyle 3158
-#define wxStyledTextCtrl_VisibleFromDocLine 3159
-#define wxStyledTextCtrl_DocLineFromVisible 3160
-#define wxStyledTextCtrl_WrapCount 3161
-#define wxStyledTextCtrl_SetFoldLevel 3162
-#define wxStyledTextCtrl_GetFoldLevel 3163
-#define wxStyledTextCtrl_GetLastChild 3164
-#define wxStyledTextCtrl_GetFoldParent 3165
-#define wxStyledTextCtrl_ShowLines 3166
-#define wxStyledTextCtrl_HideLines 3167
-#define wxStyledTextCtrl_GetLineVisible 3168
-#define wxStyledTextCtrl_SetFoldExpanded 3169
-#define wxStyledTextCtrl_GetFoldExpanded 3170
-#define wxStyledTextCtrl_ToggleFold 3171
-#define wxStyledTextCtrl_EnsureVisible 3172
-#define wxStyledTextCtrl_SetFoldFlags 3173
-#define wxStyledTextCtrl_EnsureVisibleEnforcePolicy 3174
-#define wxStyledTextCtrl_SetTabIndents 3175
-#define wxStyledTextCtrl_GetTabIndents 3176
-#define wxStyledTextCtrl_SetBackSpaceUnIndents 3177
-#define wxStyledTextCtrl_GetBackSpaceUnIndents 3178
-#define wxStyledTextCtrl_SetMouseDwellTime 3179
-#define wxStyledTextCtrl_GetMouseDwellTime 3180
-#define wxStyledTextCtrl_WordStartPosition 3181
-#define wxStyledTextCtrl_WordEndPosition 3182
-#define wxStyledTextCtrl_SetWrapMode 3183
-#define wxStyledTextCtrl_GetWrapMode 3184
-#define wxStyledTextCtrl_SetWrapVisualFlags 3185
-#define wxStyledTextCtrl_GetWrapVisualFlags 3186
-#define wxStyledTextCtrl_SetWrapVisualFlagsLocation 3187
-#define wxStyledTextCtrl_GetWrapVisualFlagsLocation 3188
-#define wxStyledTextCtrl_SetWrapStartIndent 3189
-#define wxStyledTextCtrl_GetWrapStartIndent 3190
-#define wxStyledTextCtrl_SetLayoutCache 3191
-#define wxStyledTextCtrl_GetLayoutCache 3192
-#define wxStyledTextCtrl_SetScrollWidth 3193
-#define wxStyledTextCtrl_GetScrollWidth 3194
-#define wxStyledTextCtrl_TextWidth 3195
-#define wxStyledTextCtrl_GetEndAtLastLine 3196
-#define wxStyledTextCtrl_TextHeight 3197
-#define wxStyledTextCtrl_SetUseVerticalScrollBar 3198
-#define wxStyledTextCtrl_GetUseVerticalScrollBar 3199
-#define wxStyledTextCtrl_AppendText 3200
-#define wxStyledTextCtrl_GetTwoPhaseDraw 3201
-#define wxStyledTextCtrl_SetTwoPhaseDraw 3202
-#define wxStyledTextCtrl_TargetFromSelection 3203
-#define wxStyledTextCtrl_LinesJoin 3204
-#define wxStyledTextCtrl_LinesSplit 3205
-#define wxStyledTextCtrl_SetFoldMarginColour 3206
-#define wxStyledTextCtrl_SetFoldMarginHiColour 3207
-#define wxStyledTextCtrl_LineDown 3208
-#define wxStyledTextCtrl_LineDownExtend 3209
-#define wxStyledTextCtrl_LineUp 3210
-#define wxStyledTextCtrl_LineUpExtend 3211
-#define wxStyledTextCtrl_CharLeft 3212
-#define wxStyledTextCtrl_CharLeftExtend 3213
-#define wxStyledTextCtrl_CharRight 3214
-#define wxStyledTextCtrl_CharRightExtend 3215
-#define wxStyledTextCtrl_WordLeft 3216
-#define wxStyledTextCtrl_WordLeftExtend 3217
-#define wxStyledTextCtrl_WordRight 3218
-#define wxStyledTextCtrl_WordRightExtend 3219
-#define wxStyledTextCtrl_Home 3220
-#define wxStyledTextCtrl_HomeExtend 3221
-#define wxStyledTextCtrl_LineEnd 3222
-#define wxStyledTextCtrl_LineEndExtend 3223
-#define wxStyledTextCtrl_DocumentStart 3224
-#define wxStyledTextCtrl_DocumentStartExtend 3225
-#define wxStyledTextCtrl_DocumentEnd 3226
-#define wxStyledTextCtrl_DocumentEndExtend 3227
-#define wxStyledTextCtrl_PageUp 3228
-#define wxStyledTextCtrl_PageUpExtend 3229
-#define wxStyledTextCtrl_PageDown 3230
-#define wxStyledTextCtrl_PageDownExtend 3231
-#define wxStyledTextCtrl_EditToggleOvertype 3232
-#define wxStyledTextCtrl_Cancel 3233
-#define wxStyledTextCtrl_DeleteBack 3234
-#define wxStyledTextCtrl_Tab 3235
-#define wxStyledTextCtrl_BackTab 3236
-#define wxStyledTextCtrl_NewLine 3237
-#define wxStyledTextCtrl_FormFeed 3238
-#define wxStyledTextCtrl_VCHome 3239
-#define wxStyledTextCtrl_VCHomeExtend 3240
-#define wxStyledTextCtrl_ZoomIn 3241
-#define wxStyledTextCtrl_ZoomOut 3242
-#define wxStyledTextCtrl_DelWordLeft 3243
-#define wxStyledTextCtrl_DelWordRight 3244
-#define wxStyledTextCtrl_LineCut 3245
-#define wxStyledTextCtrl_LineDelete 3246
-#define wxStyledTextCtrl_LineTranspose 3247
-#define wxStyledTextCtrl_LineDuplicate 3248
-#define wxStyledTextCtrl_LowerCase 3249
-#define wxStyledTextCtrl_UpperCase 3250
-#define wxStyledTextCtrl_LineScrollDown 3251
-#define wxStyledTextCtrl_LineScrollUp 3252
-#define wxStyledTextCtrl_DeleteBackNotLine 3253
-#define wxStyledTextCtrl_HomeDisplay 3254
-#define wxStyledTextCtrl_HomeDisplayExtend 3255
-#define wxStyledTextCtrl_LineEndDisplay 3256
-#define wxStyledTextCtrl_LineEndDisplayExtend 3257
-#define wxStyledTextCtrl_HomeWrapExtend 3258
-#define wxStyledTextCtrl_LineEndWrap 3259
-#define wxStyledTextCtrl_LineEndWrapExtend 3260
-#define wxStyledTextCtrl_VCHomeWrap 3261
-#define wxStyledTextCtrl_VCHomeWrapExtend 3262
-#define wxStyledTextCtrl_LineCopy 3263
-#define wxStyledTextCtrl_MoveCaretInsideView 3264
-#define wxStyledTextCtrl_LineLength 3265
-#define wxStyledTextCtrl_BraceHighlight 3266
-#define wxStyledTextCtrl_BraceBadLight 3267
-#define wxStyledTextCtrl_BraceMatch 3268
-#define wxStyledTextCtrl_GetViewEOL 3269
-#define wxStyledTextCtrl_SetViewEOL 3270
-#define wxStyledTextCtrl_SetModEventMask 3271
-#define wxStyledTextCtrl_GetEdgeColumn 3272
-#define wxStyledTextCtrl_SetEdgeColumn 3273
-#define wxStyledTextCtrl_SetEdgeMode 3274
-#define wxStyledTextCtrl_GetEdgeMode 3275
-#define wxStyledTextCtrl_GetEdgeColour 3276
-#define wxStyledTextCtrl_SetEdgeColour 3277
-#define wxStyledTextCtrl_SearchAnchor 3278
-#define wxStyledTextCtrl_SearchNext 3279
-#define wxStyledTextCtrl_SearchPrev 3280
-#define wxStyledTextCtrl_LinesOnScreen 3281
-#define wxStyledTextCtrl_UsePopUp 3282
-#define wxStyledTextCtrl_SelectionIsRectangle 3283
-#define wxStyledTextCtrl_SetZoom 3284
-#define wxStyledTextCtrl_GetZoom 3285
-#define wxStyledTextCtrl_GetModEventMask 3286
-#define wxStyledTextCtrl_SetSTCFocus 3287
-#define wxStyledTextCtrl_GetSTCFocus 3288
-#define wxStyledTextCtrl_SetStatus 3289
-#define wxStyledTextCtrl_GetStatus 3290
-#define wxStyledTextCtrl_SetMouseDownCaptures 3291
-#define wxStyledTextCtrl_GetMouseDownCaptures 3292
-#define wxStyledTextCtrl_SetSTCCursor 3293
-#define wxStyledTextCtrl_GetSTCCursor 3294
-#define wxStyledTextCtrl_SetControlCharSymbol 3295
-#define wxStyledTextCtrl_GetControlCharSymbol 3296
-#define wxStyledTextCtrl_WordPartLeft 3297
-#define wxStyledTextCtrl_WordPartLeftExtend 3298
-#define wxStyledTextCtrl_WordPartRight 3299
-#define wxStyledTextCtrl_WordPartRightExtend 3300
-#define wxStyledTextCtrl_SetVisiblePolicy 3301
-#define wxStyledTextCtrl_DelLineLeft 3302
-#define wxStyledTextCtrl_DelLineRight 3303
-#define wxStyledTextCtrl_GetXOffset 3304
-#define wxStyledTextCtrl_ChooseCaretX 3305
-#define wxStyledTextCtrl_SetXCaretPolicy 3306
-#define wxStyledTextCtrl_SetYCaretPolicy 3307
-#define wxStyledTextCtrl_GetPrintWrapMode 3308
-#define wxStyledTextCtrl_SetHotspotActiveForeground 3309
-#define wxStyledTextCtrl_SetHotspotActiveBackground 3310
-#define wxStyledTextCtrl_SetHotspotActiveUnderline 3311
-#define wxStyledTextCtrl_SetHotspotSingleLine 3312
-#define wxStyledTextCtrl_ParaDownExtend 3313
-#define wxStyledTextCtrl_ParaUp 3314
-#define wxStyledTextCtrl_ParaUpExtend 3315
-#define wxStyledTextCtrl_PositionBefore 3316
-#define wxStyledTextCtrl_PositionAfter 3317
-#define wxStyledTextCtrl_CopyRange 3318
-#define wxStyledTextCtrl_CopyText 3319
-#define wxStyledTextCtrl_SetSelectionMode 3320
-#define wxStyledTextCtrl_GetSelectionMode 3321
-#define wxStyledTextCtrl_LineDownRectExtend 3322
-#define wxStyledTextCtrl_LineUpRectExtend 3323
-#define wxStyledTextCtrl_CharLeftRectExtend 3324
-#define wxStyledTextCtrl_CharRightRectExtend 3325
-#define wxStyledTextCtrl_HomeRectExtend 3326
-#define wxStyledTextCtrl_VCHomeRectExtend 3327
-#define wxStyledTextCtrl_LineEndRectExtend 3328
-#define wxStyledTextCtrl_PageUpRectExtend 3329
-#define wxStyledTextCtrl_PageDownRectExtend 3330
-#define wxStyledTextCtrl_StutteredPageUp 3331
-#define wxStyledTextCtrl_StutteredPageUpExtend 3332
-#define wxStyledTextCtrl_StutteredPageDown 3333
-#define wxStyledTextCtrl_StutteredPageDownExtend 3334
-#define wxStyledTextCtrl_WordLeftEnd 3335
-#define wxStyledTextCtrl_WordLeftEndExtend 3336
-#define wxStyledTextCtrl_WordRightEnd 3337
-#define wxStyledTextCtrl_WordRightEndExtend 3338
-#define wxStyledTextCtrl_SetWhitespaceChars 3339
-#define wxStyledTextCtrl_SetCharsDefault 3340
-#define wxStyledTextCtrl_AutoCompGetCurrent 3341
-#define wxStyledTextCtrl_Allocate 3342
-#define wxStyledTextCtrl_FindColumn 3343
-#define wxStyledTextCtrl_GetCaretSticky 3344
-#define wxStyledTextCtrl_SetCaretSticky 3345
-#define wxStyledTextCtrl_ToggleCaretSticky 3346
-#define wxStyledTextCtrl_SetPasteConvertEndings 3347
-#define wxStyledTextCtrl_GetPasteConvertEndings 3348
-#define wxStyledTextCtrl_SelectionDuplicate 3349
-#define wxStyledTextCtrl_SetCaretLineBackAlpha 3350
-#define wxStyledTextCtrl_GetCaretLineBackAlpha 3351
-#define wxStyledTextCtrl_StartRecord 3352
-#define wxStyledTextCtrl_StopRecord 3353
-#define wxStyledTextCtrl_SetLexer 3354
-#define wxStyledTextCtrl_GetLexer 3355
-#define wxStyledTextCtrl_Colourise 3356
-#define wxStyledTextCtrl_SetProperty 3357
-#define wxStyledTextCtrl_SetKeyWords 3358
-#define wxStyledTextCtrl_SetLexerLanguage 3359
-#define wxStyledTextCtrl_GetProperty 3360
-#define wxStyledTextCtrl_GetStyleBitsNeeded 3361
-#define wxStyledTextCtrl_GetCurrentLine 3362
-#define wxStyledTextCtrl_StyleSetSpec 3363
-#define wxStyledTextCtrl_StyleSetFont 3364
-#define wxStyledTextCtrl_StyleSetFontAttr 3365
-#define wxStyledTextCtrl_StyleSetCharacterSet 3366
-#define wxStyledTextCtrl_StyleSetFontEncoding 3367
-#define wxStyledTextCtrl_CmdKeyExecute 3368
-#define wxStyledTextCtrl_SetMargins 3369
-#define wxStyledTextCtrl_GetSelection 3370
-#define wxStyledTextCtrl_PointFromPosition 3371
-#define wxStyledTextCtrl_ScrollToLine 3372
-#define wxStyledTextCtrl_ScrollToColumn 3373
-#define wxStyledTextCtrl_SetVScrollBar 3374
-#define wxStyledTextCtrl_SetHScrollBar 3375
-#define wxStyledTextCtrl_GetLastKeydownProcessed 3376
-#define wxStyledTextCtrl_SetLastKeydownProcessed 3377
-#define wxStyledTextCtrl_SaveFile 3378
-#define wxStyledTextCtrl_LoadFile 3379
-#define wxStyledTextCtrl_DoDragOver 3380
-#define wxStyledTextCtrl_DoDropText 3381
-#define wxStyledTextCtrl_GetUseAntiAliasing 3382
-#define wxStyledTextCtrl_AddTextRaw 3383
-#define wxStyledTextCtrl_InsertTextRaw 3384
-#define wxStyledTextCtrl_GetCurLineRaw 3385
-#define wxStyledTextCtrl_GetLineRaw 3386
-#define wxStyledTextCtrl_GetSelectedTextRaw 3387
-#define wxStyledTextCtrl_GetTextRangeRaw 3388
-#define wxStyledTextCtrl_SetTextRaw 3389
-#define wxStyledTextCtrl_GetTextRaw 3390
-#define wxStyledTextCtrl_AppendTextRaw 3391
-#define wxArtProvider_GetBitmap 3392
-#define wxArtProvider_GetIcon 3393
-#define wxTreeEvent_GetKeyCode 3394
-#define wxTreeEvent_GetItem 3395
-#define wxTreeEvent_GetKeyEvent 3396
-#define wxTreeEvent_GetLabel 3397
-#define wxTreeEvent_GetOldItem 3398
-#define wxTreeEvent_GetPoint 3399
-#define wxTreeEvent_IsEditCancelled 3400
-#define wxTreeEvent_SetToolTip 3401
-#define wxNotebookEvent_GetOldSelection 3402
-#define wxNotebookEvent_GetSelection 3403
-#define wxNotebookEvent_SetOldSelection 3404
-#define wxNotebookEvent_SetSelection 3405
-#define wxFileDataObject_new 3406
-#define wxFileDataObject_AddFile 3407
-#define wxFileDataObject_GetFilenames 3408
-#define wxFileDataObject_destroy 3409
-#define wxTextDataObject_new 3410
-#define wxTextDataObject_GetTextLength 3411
-#define wxTextDataObject_GetText 3412
-#define wxTextDataObject_SetText 3413
-#define wxTextDataObject_destroy 3414
-#define wxBitmapDataObject_new_1_1 3415
-#define wxBitmapDataObject_new_1_0 3416
-#define wxBitmapDataObject_GetBitmap 3417
-#define wxBitmapDataObject_SetBitmap 3418
-#define wxBitmapDataObject_destroy 3419
-#define wxClipboard_new 3421
-#define wxClipboard_destruct 3422
-#define wxClipboard_AddData 3423
-#define wxClipboard_Clear 3424
-#define wxClipboard_Close 3425
-#define wxClipboard_Flush 3426
-#define wxClipboard_GetData 3427
-#define wxClipboard_IsOpened 3428
-#define wxClipboard_Open 3429
-#define wxClipboard_SetData 3430
-#define wxClipboard_UsePrimarySelection 3432
-#define wxClipboard_IsSupported 3433
-#define wxClipboard_Get 3434
-#define wxSpinEvent_GetPosition 3435
-#define wxSpinEvent_SetPosition 3436
-#define wxSplitterWindow_new_0 3437
-#define wxSplitterWindow_new_2 3438
-#define wxSplitterWindow_destruct 3439
-#define wxSplitterWindow_Create 3440
-#define wxSplitterWindow_GetMinimumPaneSize 3441
-#define wxSplitterWindow_GetSashGravity 3442
-#define wxSplitterWindow_GetSashPosition 3443
-#define wxSplitterWindow_GetSplitMode 3444
-#define wxSplitterWindow_GetWindow1 3445
-#define wxSplitterWindow_GetWindow2 3446
-#define wxSplitterWindow_Initialize 3447
-#define wxSplitterWindow_IsSplit 3448
-#define wxSplitterWindow_ReplaceWindow 3449
-#define wxSplitterWindow_SetSashGravity 3450
-#define wxSplitterWindow_SetSashPosition 3451
-#define wxSplitterWindow_SetSashSize 3452
-#define wxSplitterWindow_SetMinimumPaneSize 3453
-#define wxSplitterWindow_SetSplitMode 3454
-#define wxSplitterWindow_SplitHorizontally 3455
-#define wxSplitterWindow_SplitVertically 3456
-#define wxSplitterWindow_Unsplit 3457
-#define wxSplitterWindow_UpdateSize 3458
-#define wxSplitterEvent_GetSashPosition 3459
-#define wxSplitterEvent_GetX 3460
-#define wxSplitterEvent_GetY 3461
-#define wxSplitterEvent_GetWindowBeingRemoved 3462
-#define wxSplitterEvent_SetSashPosition 3463
-#define wxHtmlWindow_new_0 3464
-#define wxHtmlWindow_new_2 3465
-#define wxHtmlWindow_AppendToPage 3466
-#define wxHtmlWindow_GetOpenedAnchor 3467
-#define wxHtmlWindow_GetOpenedPage 3468
-#define wxHtmlWindow_GetOpenedPageTitle 3469
-#define wxHtmlWindow_GetRelatedFrame 3470
-#define wxHtmlWindow_HistoryBack 3471
-#define wxHtmlWindow_HistoryCanBack 3472
-#define wxHtmlWindow_HistoryCanForward 3473
-#define wxHtmlWindow_HistoryClear 3474
-#define wxHtmlWindow_HistoryForward 3475
-#define wxHtmlWindow_LoadFile 3476
-#define wxHtmlWindow_LoadPage 3477
-#define wxHtmlWindow_SelectAll 3478
-#define wxHtmlWindow_SelectionToText 3479
-#define wxHtmlWindow_SelectLine 3480
-#define wxHtmlWindow_SelectWord 3481
-#define wxHtmlWindow_SetBorders 3482
-#define wxHtmlWindow_SetFonts 3483
-#define wxHtmlWindow_SetPage 3484
-#define wxHtmlWindow_SetRelatedFrame 3485
-#define wxHtmlWindow_SetRelatedStatusBar 3486
-#define wxHtmlWindow_ToText 3487
-#define wxHtmlWindow_destroy 3488
-#define wxHtmlLinkEvent_GetLinkInfo 3489
-#define wxSystemSettings_GetColour 3490
-#define wxSystemSettings_GetFont 3491
-#define wxSystemSettings_GetMetric 3492
-#define wxSystemSettings_GetScreenType 3493
-#define wxSystemOptions_GetOption 3494
-#define wxSystemOptions_GetOptionInt 3495
-#define wxSystemOptions_HasOption 3496
-#define wxSystemOptions_IsFalse 3497
-#define wxSystemOptions_SetOption_2_1 3498
-#define wxSystemOptions_SetOption_2_0 3499
-#define wxAuiNotebookEvent_SetSelection 3500
-#define wxAuiNotebookEvent_GetSelection 3501
-#define wxAuiNotebookEvent_SetOldSelection 3502
-#define wxAuiNotebookEvent_GetOldSelection 3503
-#define wxAuiNotebookEvent_SetDragSource 3504
-#define wxAuiNotebookEvent_GetDragSource 3505
-#define wxAuiManagerEvent_SetManager 3506
-#define wxAuiManagerEvent_GetManager 3507
-#define wxAuiManagerEvent_SetPane 3508
-#define wxAuiManagerEvent_GetPane 3509
-#define wxAuiManagerEvent_SetButton 3510
-#define wxAuiManagerEvent_GetButton 3511
-#define wxAuiManagerEvent_SetDC 3512
-#define wxAuiManagerEvent_GetDC 3513
-#define wxAuiManagerEvent_Veto 3514
-#define wxAuiManagerEvent_GetVeto 3515
-#define wxAuiManagerEvent_SetCanVeto 3516
-#define wxAuiManagerEvent_CanVeto 3517
-#define wxLogNull_new 3518
-#define wxLogNull_destroy 3519
-#define wxTaskBarIcon_new 3520
-#define wxTaskBarIcon_destruct 3521
-#define wxTaskBarIcon_PopupMenu 3522
-#define wxTaskBarIcon_RemoveIcon 3523
-#define wxTaskBarIcon_SetIcon 3524
+#define gdicmn_wxDisplaySize 2927
+#define gdicmn_wxSetCursor 2928
+#define wxPrintout_new 2929
+#define wxPrintout_destruct 2930
+#define wxPrintout_GetDC 2931
+#define wxPrintout_GetPageSizeMM 2932
+#define wxPrintout_GetPageSizePixels 2933
+#define wxPrintout_GetPaperRectPixels 2934
+#define wxPrintout_GetPPIPrinter 2935
+#define wxPrintout_GetPPIScreen 2936
+#define wxPrintout_GetTitle 2937
+#define wxPrintout_IsPreview 2938
+#define wxPrintout_FitThisSizeToPaper 2939
+#define wxPrintout_FitThisSizeToPage 2940
+#define wxPrintout_FitThisSizeToPageMargins 2941
+#define wxPrintout_MapScreenSizeToPaper 2942
+#define wxPrintout_MapScreenSizeToPage 2943
+#define wxPrintout_MapScreenSizeToPageMargins 2944
+#define wxPrintout_MapScreenSizeToDevice 2945
+#define wxPrintout_GetLogicalPaperRect 2946
+#define wxPrintout_GetLogicalPageRect 2947
+#define wxPrintout_GetLogicalPageMarginsRect 2948
+#define wxPrintout_SetLogicalOrigin 2949
+#define wxPrintout_OffsetLogicalOrigin 2950
+#define wxStyledTextCtrl_new_2 2951
+#define wxStyledTextCtrl_new_0 2952
+#define wxStyledTextCtrl_destruct 2953
+#define wxStyledTextCtrl_Create 2954
+#define wxStyledTextCtrl_AddText 2955
+#define wxStyledTextCtrl_AddStyledText 2956
+#define wxStyledTextCtrl_InsertText 2957
+#define wxStyledTextCtrl_ClearAll 2958
+#define wxStyledTextCtrl_ClearDocumentStyle 2959
+#define wxStyledTextCtrl_GetLength 2960
+#define wxStyledTextCtrl_GetCharAt 2961
+#define wxStyledTextCtrl_GetCurrentPos 2962
+#define wxStyledTextCtrl_GetAnchor 2963
+#define wxStyledTextCtrl_GetStyleAt 2964
+#define wxStyledTextCtrl_Redo 2965
+#define wxStyledTextCtrl_SetUndoCollection 2966
+#define wxStyledTextCtrl_SelectAll 2967
+#define wxStyledTextCtrl_SetSavePoint 2968
+#define wxStyledTextCtrl_GetStyledText 2969
+#define wxStyledTextCtrl_CanRedo 2970
+#define wxStyledTextCtrl_MarkerLineFromHandle 2971
+#define wxStyledTextCtrl_MarkerDeleteHandle 2972
+#define wxStyledTextCtrl_GetUndoCollection 2973
+#define wxStyledTextCtrl_GetViewWhiteSpace 2974
+#define wxStyledTextCtrl_SetViewWhiteSpace 2975
+#define wxStyledTextCtrl_PositionFromPoint 2976
+#define wxStyledTextCtrl_PositionFromPointClose 2977
+#define wxStyledTextCtrl_GotoLine 2978
+#define wxStyledTextCtrl_GotoPos 2979
+#define wxStyledTextCtrl_SetAnchor 2980
+#define wxStyledTextCtrl_GetCurLine 2981
+#define wxStyledTextCtrl_GetEndStyled 2982
+#define wxStyledTextCtrl_ConvertEOLs 2983
+#define wxStyledTextCtrl_GetEOLMode 2984
+#define wxStyledTextCtrl_SetEOLMode 2985
+#define wxStyledTextCtrl_StartStyling 2986
+#define wxStyledTextCtrl_SetStyling 2987
+#define wxStyledTextCtrl_GetBufferedDraw 2988
+#define wxStyledTextCtrl_SetBufferedDraw 2989
+#define wxStyledTextCtrl_SetTabWidth 2990
+#define wxStyledTextCtrl_GetTabWidth 2991
+#define wxStyledTextCtrl_SetCodePage 2992
+#define wxStyledTextCtrl_MarkerDefine 2993
+#define wxStyledTextCtrl_MarkerSetForeground 2994
+#define wxStyledTextCtrl_MarkerSetBackground 2995
+#define wxStyledTextCtrl_MarkerAdd 2996
+#define wxStyledTextCtrl_MarkerDelete 2997
+#define wxStyledTextCtrl_MarkerDeleteAll 2998
+#define wxStyledTextCtrl_MarkerGet 2999
+#define wxStyledTextCtrl_MarkerNext 3000
+#define wxStyledTextCtrl_MarkerPrevious 3001
+#define wxStyledTextCtrl_MarkerDefineBitmap 3002
+#define wxStyledTextCtrl_MarkerAddSet 3003
+#define wxStyledTextCtrl_MarkerSetAlpha 3004
+#define wxStyledTextCtrl_SetMarginType 3005
+#define wxStyledTextCtrl_GetMarginType 3006
+#define wxStyledTextCtrl_SetMarginWidth 3007
+#define wxStyledTextCtrl_GetMarginWidth 3008
+#define wxStyledTextCtrl_SetMarginMask 3009
+#define wxStyledTextCtrl_GetMarginMask 3010
+#define wxStyledTextCtrl_SetMarginSensitive 3011
+#define wxStyledTextCtrl_GetMarginSensitive 3012
+#define wxStyledTextCtrl_StyleClearAll 3013
+#define wxStyledTextCtrl_StyleSetForeground 3014
+#define wxStyledTextCtrl_StyleSetBackground 3015
+#define wxStyledTextCtrl_StyleSetBold 3016
+#define wxStyledTextCtrl_StyleSetItalic 3017
+#define wxStyledTextCtrl_StyleSetSize 3018
+#define wxStyledTextCtrl_StyleSetFaceName 3019
+#define wxStyledTextCtrl_StyleSetEOLFilled 3020
+#define wxStyledTextCtrl_StyleResetDefault 3021
+#define wxStyledTextCtrl_StyleSetUnderline 3022
+#define wxStyledTextCtrl_StyleSetCase 3023
+#define wxStyledTextCtrl_StyleSetHotSpot 3024
+#define wxStyledTextCtrl_SetSelForeground 3025
+#define wxStyledTextCtrl_SetSelBackground 3026
+#define wxStyledTextCtrl_GetSelAlpha 3027
+#define wxStyledTextCtrl_SetSelAlpha 3028
+#define wxStyledTextCtrl_SetCaretForeground 3029
+#define wxStyledTextCtrl_CmdKeyAssign 3030
+#define wxStyledTextCtrl_CmdKeyClear 3031
+#define wxStyledTextCtrl_CmdKeyClearAll 3032
+#define wxStyledTextCtrl_SetStyleBytes 3033
+#define wxStyledTextCtrl_StyleSetVisible 3034
+#define wxStyledTextCtrl_GetCaretPeriod 3035
+#define wxStyledTextCtrl_SetCaretPeriod 3036
+#define wxStyledTextCtrl_SetWordChars 3037
+#define wxStyledTextCtrl_BeginUndoAction 3038
+#define wxStyledTextCtrl_EndUndoAction 3039
+#define wxStyledTextCtrl_IndicatorSetStyle 3040
+#define wxStyledTextCtrl_IndicatorGetStyle 3041
+#define wxStyledTextCtrl_IndicatorSetForeground 3042
+#define wxStyledTextCtrl_IndicatorGetForeground 3043
+#define wxStyledTextCtrl_SetWhitespaceForeground 3044
+#define wxStyledTextCtrl_SetWhitespaceBackground 3045
+#define wxStyledTextCtrl_GetStyleBits 3046
+#define wxStyledTextCtrl_SetLineState 3047
+#define wxStyledTextCtrl_GetLineState 3048
+#define wxStyledTextCtrl_GetMaxLineState 3049
+#define wxStyledTextCtrl_GetCaretLineVisible 3050
+#define wxStyledTextCtrl_SetCaretLineVisible 3051
+#define wxStyledTextCtrl_GetCaretLineBackground 3052
+#define wxStyledTextCtrl_SetCaretLineBackground 3053
+#define wxStyledTextCtrl_AutoCompShow 3054
+#define wxStyledTextCtrl_AutoCompCancel 3055
+#define wxStyledTextCtrl_AutoCompActive 3056
+#define wxStyledTextCtrl_AutoCompPosStart 3057
+#define wxStyledTextCtrl_AutoCompComplete 3058
+#define wxStyledTextCtrl_AutoCompStops 3059
+#define wxStyledTextCtrl_AutoCompSetSeparator 3060
+#define wxStyledTextCtrl_AutoCompGetSeparator 3061
+#define wxStyledTextCtrl_AutoCompSelect 3062
+#define wxStyledTextCtrl_AutoCompSetCancelAtStart 3063
+#define wxStyledTextCtrl_AutoCompGetCancelAtStart 3064
+#define wxStyledTextCtrl_AutoCompSetFillUps 3065
+#define wxStyledTextCtrl_AutoCompSetChooseSingle 3066
+#define wxStyledTextCtrl_AutoCompGetChooseSingle 3067
+#define wxStyledTextCtrl_AutoCompSetIgnoreCase 3068
+#define wxStyledTextCtrl_AutoCompGetIgnoreCase 3069
+#define wxStyledTextCtrl_UserListShow 3070
+#define wxStyledTextCtrl_AutoCompSetAutoHide 3071
+#define wxStyledTextCtrl_AutoCompGetAutoHide 3072
+#define wxStyledTextCtrl_AutoCompSetDropRestOfWord 3073
+#define wxStyledTextCtrl_AutoCompGetDropRestOfWord 3074
+#define wxStyledTextCtrl_RegisterImage 3075
+#define wxStyledTextCtrl_ClearRegisteredImages 3076
+#define wxStyledTextCtrl_AutoCompGetTypeSeparator 3077
+#define wxStyledTextCtrl_AutoCompSetTypeSeparator 3078
+#define wxStyledTextCtrl_AutoCompSetMaxWidth 3079
+#define wxStyledTextCtrl_AutoCompGetMaxWidth 3080
+#define wxStyledTextCtrl_AutoCompSetMaxHeight 3081
+#define wxStyledTextCtrl_AutoCompGetMaxHeight 3082
+#define wxStyledTextCtrl_SetIndent 3083
+#define wxStyledTextCtrl_GetIndent 3084
+#define wxStyledTextCtrl_SetUseTabs 3085
+#define wxStyledTextCtrl_GetUseTabs 3086
+#define wxStyledTextCtrl_SetLineIndentation 3087
+#define wxStyledTextCtrl_GetLineIndentation 3088
+#define wxStyledTextCtrl_GetLineIndentPosition 3089
+#define wxStyledTextCtrl_GetColumn 3090
+#define wxStyledTextCtrl_SetUseHorizontalScrollBar 3091
+#define wxStyledTextCtrl_GetUseHorizontalScrollBar 3092
+#define wxStyledTextCtrl_SetIndentationGuides 3093
+#define wxStyledTextCtrl_GetIndentationGuides 3094
+#define wxStyledTextCtrl_SetHighlightGuide 3095
+#define wxStyledTextCtrl_GetHighlightGuide 3096
+#define wxStyledTextCtrl_GetLineEndPosition 3097
+#define wxStyledTextCtrl_GetCodePage 3098
+#define wxStyledTextCtrl_GetCaretForeground 3099
+#define wxStyledTextCtrl_GetReadOnly 3100
+#define wxStyledTextCtrl_SetCurrentPos 3101
+#define wxStyledTextCtrl_SetSelectionStart 3102
+#define wxStyledTextCtrl_GetSelectionStart 3103
+#define wxStyledTextCtrl_SetSelectionEnd 3104
+#define wxStyledTextCtrl_GetSelectionEnd 3105
+#define wxStyledTextCtrl_SetPrintMagnification 3106
+#define wxStyledTextCtrl_GetPrintMagnification 3107
+#define wxStyledTextCtrl_SetPrintColourMode 3108
+#define wxStyledTextCtrl_GetPrintColourMode 3109
+#define wxStyledTextCtrl_FindText 3110
+#define wxStyledTextCtrl_FormatRange 3111
+#define wxStyledTextCtrl_GetFirstVisibleLine 3112
+#define wxStyledTextCtrl_GetLine 3113
+#define wxStyledTextCtrl_GetLineCount 3114
+#define wxStyledTextCtrl_SetMarginLeft 3115
+#define wxStyledTextCtrl_GetMarginLeft 3116
+#define wxStyledTextCtrl_SetMarginRight 3117
+#define wxStyledTextCtrl_GetMarginRight 3118
+#define wxStyledTextCtrl_GetModify 3119
+#define wxStyledTextCtrl_SetSelection 3120
+#define wxStyledTextCtrl_GetSelectedText 3121
+#define wxStyledTextCtrl_GetTextRange 3122
+#define wxStyledTextCtrl_HideSelection 3123
+#define wxStyledTextCtrl_LineFromPosition 3124
+#define wxStyledTextCtrl_PositionFromLine 3125
+#define wxStyledTextCtrl_LineScroll 3126
+#define wxStyledTextCtrl_EnsureCaretVisible 3127
+#define wxStyledTextCtrl_ReplaceSelection 3128
+#define wxStyledTextCtrl_SetReadOnly 3129
+#define wxStyledTextCtrl_CanPaste 3130
+#define wxStyledTextCtrl_CanUndo 3131
+#define wxStyledTextCtrl_EmptyUndoBuffer 3132
+#define wxStyledTextCtrl_Undo 3133
+#define wxStyledTextCtrl_Cut 3134
+#define wxStyledTextCtrl_Copy 3135
+#define wxStyledTextCtrl_Paste 3136
+#define wxStyledTextCtrl_Clear 3137
+#define wxStyledTextCtrl_SetText 3138
+#define wxStyledTextCtrl_GetText 3139
+#define wxStyledTextCtrl_GetTextLength 3140
+#define wxStyledTextCtrl_GetOvertype 3141
+#define wxStyledTextCtrl_SetCaretWidth 3142
+#define wxStyledTextCtrl_GetCaretWidth 3143
+#define wxStyledTextCtrl_SetTargetStart 3144
+#define wxStyledTextCtrl_GetTargetStart 3145
+#define wxStyledTextCtrl_SetTargetEnd 3146
+#define wxStyledTextCtrl_GetTargetEnd 3147
+#define wxStyledTextCtrl_ReplaceTarget 3148
+#define wxStyledTextCtrl_SearchInTarget 3149
+#define wxStyledTextCtrl_SetSearchFlags 3150
+#define wxStyledTextCtrl_GetSearchFlags 3151
+#define wxStyledTextCtrl_CallTipShow 3152
+#define wxStyledTextCtrl_CallTipCancel 3153
+#define wxStyledTextCtrl_CallTipActive 3154
+#define wxStyledTextCtrl_CallTipPosAtStart 3155
+#define wxStyledTextCtrl_CallTipSetHighlight 3156
+#define wxStyledTextCtrl_CallTipSetBackground 3157
+#define wxStyledTextCtrl_CallTipSetForeground 3158
+#define wxStyledTextCtrl_CallTipSetForegroundHighlight 3159
+#define wxStyledTextCtrl_CallTipUseStyle 3160
+#define wxStyledTextCtrl_VisibleFromDocLine 3161
+#define wxStyledTextCtrl_DocLineFromVisible 3162
+#define wxStyledTextCtrl_WrapCount 3163
+#define wxStyledTextCtrl_SetFoldLevel 3164
+#define wxStyledTextCtrl_GetFoldLevel 3165
+#define wxStyledTextCtrl_GetLastChild 3166
+#define wxStyledTextCtrl_GetFoldParent 3167
+#define wxStyledTextCtrl_ShowLines 3168
+#define wxStyledTextCtrl_HideLines 3169
+#define wxStyledTextCtrl_GetLineVisible 3170
+#define wxStyledTextCtrl_SetFoldExpanded 3171
+#define wxStyledTextCtrl_GetFoldExpanded 3172
+#define wxStyledTextCtrl_ToggleFold 3173
+#define wxStyledTextCtrl_EnsureVisible 3174
+#define wxStyledTextCtrl_SetFoldFlags 3175
+#define wxStyledTextCtrl_EnsureVisibleEnforcePolicy 3176
+#define wxStyledTextCtrl_SetTabIndents 3177
+#define wxStyledTextCtrl_GetTabIndents 3178
+#define wxStyledTextCtrl_SetBackSpaceUnIndents 3179
+#define wxStyledTextCtrl_GetBackSpaceUnIndents 3180
+#define wxStyledTextCtrl_SetMouseDwellTime 3181
+#define wxStyledTextCtrl_GetMouseDwellTime 3182
+#define wxStyledTextCtrl_WordStartPosition 3183
+#define wxStyledTextCtrl_WordEndPosition 3184
+#define wxStyledTextCtrl_SetWrapMode 3185
+#define wxStyledTextCtrl_GetWrapMode 3186
+#define wxStyledTextCtrl_SetWrapVisualFlags 3187
+#define wxStyledTextCtrl_GetWrapVisualFlags 3188
+#define wxStyledTextCtrl_SetWrapVisualFlagsLocation 3189
+#define wxStyledTextCtrl_GetWrapVisualFlagsLocation 3190
+#define wxStyledTextCtrl_SetWrapStartIndent 3191
+#define wxStyledTextCtrl_GetWrapStartIndent 3192
+#define wxStyledTextCtrl_SetLayoutCache 3193
+#define wxStyledTextCtrl_GetLayoutCache 3194
+#define wxStyledTextCtrl_SetScrollWidth 3195
+#define wxStyledTextCtrl_GetScrollWidth 3196
+#define wxStyledTextCtrl_TextWidth 3197
+#define wxStyledTextCtrl_GetEndAtLastLine 3198
+#define wxStyledTextCtrl_TextHeight 3199
+#define wxStyledTextCtrl_SetUseVerticalScrollBar 3200
+#define wxStyledTextCtrl_GetUseVerticalScrollBar 3201
+#define wxStyledTextCtrl_AppendText 3202
+#define wxStyledTextCtrl_GetTwoPhaseDraw 3203
+#define wxStyledTextCtrl_SetTwoPhaseDraw 3204
+#define wxStyledTextCtrl_TargetFromSelection 3205
+#define wxStyledTextCtrl_LinesJoin 3206
+#define wxStyledTextCtrl_LinesSplit 3207
+#define wxStyledTextCtrl_SetFoldMarginColour 3208
+#define wxStyledTextCtrl_SetFoldMarginHiColour 3209
+#define wxStyledTextCtrl_LineDown 3210
+#define wxStyledTextCtrl_LineDownExtend 3211
+#define wxStyledTextCtrl_LineUp 3212
+#define wxStyledTextCtrl_LineUpExtend 3213
+#define wxStyledTextCtrl_CharLeft 3214
+#define wxStyledTextCtrl_CharLeftExtend 3215
+#define wxStyledTextCtrl_CharRight 3216
+#define wxStyledTextCtrl_CharRightExtend 3217
+#define wxStyledTextCtrl_WordLeft 3218
+#define wxStyledTextCtrl_WordLeftExtend 3219
+#define wxStyledTextCtrl_WordRight 3220
+#define wxStyledTextCtrl_WordRightExtend 3221
+#define wxStyledTextCtrl_Home 3222
+#define wxStyledTextCtrl_HomeExtend 3223
+#define wxStyledTextCtrl_LineEnd 3224
+#define wxStyledTextCtrl_LineEndExtend 3225
+#define wxStyledTextCtrl_DocumentStart 3226
+#define wxStyledTextCtrl_DocumentStartExtend 3227
+#define wxStyledTextCtrl_DocumentEnd 3228
+#define wxStyledTextCtrl_DocumentEndExtend 3229
+#define wxStyledTextCtrl_PageUp 3230
+#define wxStyledTextCtrl_PageUpExtend 3231
+#define wxStyledTextCtrl_PageDown 3232
+#define wxStyledTextCtrl_PageDownExtend 3233
+#define wxStyledTextCtrl_EditToggleOvertype 3234
+#define wxStyledTextCtrl_Cancel 3235
+#define wxStyledTextCtrl_DeleteBack 3236
+#define wxStyledTextCtrl_Tab 3237
+#define wxStyledTextCtrl_BackTab 3238
+#define wxStyledTextCtrl_NewLine 3239
+#define wxStyledTextCtrl_FormFeed 3240
+#define wxStyledTextCtrl_VCHome 3241
+#define wxStyledTextCtrl_VCHomeExtend 3242
+#define wxStyledTextCtrl_ZoomIn 3243
+#define wxStyledTextCtrl_ZoomOut 3244
+#define wxStyledTextCtrl_DelWordLeft 3245
+#define wxStyledTextCtrl_DelWordRight 3246
+#define wxStyledTextCtrl_LineCut 3247
+#define wxStyledTextCtrl_LineDelete 3248
+#define wxStyledTextCtrl_LineTranspose 3249
+#define wxStyledTextCtrl_LineDuplicate 3250
+#define wxStyledTextCtrl_LowerCase 3251
+#define wxStyledTextCtrl_UpperCase 3252
+#define wxStyledTextCtrl_LineScrollDown 3253
+#define wxStyledTextCtrl_LineScrollUp 3254
+#define wxStyledTextCtrl_DeleteBackNotLine 3255
+#define wxStyledTextCtrl_HomeDisplay 3256
+#define wxStyledTextCtrl_HomeDisplayExtend 3257
+#define wxStyledTextCtrl_LineEndDisplay 3258
+#define wxStyledTextCtrl_LineEndDisplayExtend 3259
+#define wxStyledTextCtrl_HomeWrapExtend 3260
+#define wxStyledTextCtrl_LineEndWrap 3261
+#define wxStyledTextCtrl_LineEndWrapExtend 3262
+#define wxStyledTextCtrl_VCHomeWrap 3263
+#define wxStyledTextCtrl_VCHomeWrapExtend 3264
+#define wxStyledTextCtrl_LineCopy 3265
+#define wxStyledTextCtrl_MoveCaretInsideView 3266
+#define wxStyledTextCtrl_LineLength 3267
+#define wxStyledTextCtrl_BraceHighlight 3268
+#define wxStyledTextCtrl_BraceBadLight 3269
+#define wxStyledTextCtrl_BraceMatch 3270
+#define wxStyledTextCtrl_GetViewEOL 3271
+#define wxStyledTextCtrl_SetViewEOL 3272
+#define wxStyledTextCtrl_SetModEventMask 3273
+#define wxStyledTextCtrl_GetEdgeColumn 3274
+#define wxStyledTextCtrl_SetEdgeColumn 3275
+#define wxStyledTextCtrl_SetEdgeMode 3276
+#define wxStyledTextCtrl_GetEdgeMode 3277
+#define wxStyledTextCtrl_GetEdgeColour 3278
+#define wxStyledTextCtrl_SetEdgeColour 3279
+#define wxStyledTextCtrl_SearchAnchor 3280
+#define wxStyledTextCtrl_SearchNext 3281
+#define wxStyledTextCtrl_SearchPrev 3282
+#define wxStyledTextCtrl_LinesOnScreen 3283
+#define wxStyledTextCtrl_UsePopUp 3284
+#define wxStyledTextCtrl_SelectionIsRectangle 3285
+#define wxStyledTextCtrl_SetZoom 3286
+#define wxStyledTextCtrl_GetZoom 3287
+#define wxStyledTextCtrl_GetModEventMask 3288
+#define wxStyledTextCtrl_SetSTCFocus 3289
+#define wxStyledTextCtrl_GetSTCFocus 3290
+#define wxStyledTextCtrl_SetStatus 3291
+#define wxStyledTextCtrl_GetStatus 3292
+#define wxStyledTextCtrl_SetMouseDownCaptures 3293
+#define wxStyledTextCtrl_GetMouseDownCaptures 3294
+#define wxStyledTextCtrl_SetSTCCursor 3295
+#define wxStyledTextCtrl_GetSTCCursor 3296
+#define wxStyledTextCtrl_SetControlCharSymbol 3297
+#define wxStyledTextCtrl_GetControlCharSymbol 3298
+#define wxStyledTextCtrl_WordPartLeft 3299
+#define wxStyledTextCtrl_WordPartLeftExtend 3300
+#define wxStyledTextCtrl_WordPartRight 3301
+#define wxStyledTextCtrl_WordPartRightExtend 3302
+#define wxStyledTextCtrl_SetVisiblePolicy 3303
+#define wxStyledTextCtrl_DelLineLeft 3304
+#define wxStyledTextCtrl_DelLineRight 3305
+#define wxStyledTextCtrl_GetXOffset 3306
+#define wxStyledTextCtrl_ChooseCaretX 3307
+#define wxStyledTextCtrl_SetXCaretPolicy 3308
+#define wxStyledTextCtrl_SetYCaretPolicy 3309
+#define wxStyledTextCtrl_GetPrintWrapMode 3310
+#define wxStyledTextCtrl_SetHotspotActiveForeground 3311
+#define wxStyledTextCtrl_SetHotspotActiveBackground 3312
+#define wxStyledTextCtrl_SetHotspotActiveUnderline 3313
+#define wxStyledTextCtrl_SetHotspotSingleLine 3314
+#define wxStyledTextCtrl_ParaDownExtend 3315
+#define wxStyledTextCtrl_ParaUp 3316
+#define wxStyledTextCtrl_ParaUpExtend 3317
+#define wxStyledTextCtrl_PositionBefore 3318
+#define wxStyledTextCtrl_PositionAfter 3319
+#define wxStyledTextCtrl_CopyRange 3320
+#define wxStyledTextCtrl_CopyText 3321
+#define wxStyledTextCtrl_SetSelectionMode 3322
+#define wxStyledTextCtrl_GetSelectionMode 3323
+#define wxStyledTextCtrl_LineDownRectExtend 3324
+#define wxStyledTextCtrl_LineUpRectExtend 3325
+#define wxStyledTextCtrl_CharLeftRectExtend 3326
+#define wxStyledTextCtrl_CharRightRectExtend 3327
+#define wxStyledTextCtrl_HomeRectExtend 3328
+#define wxStyledTextCtrl_VCHomeRectExtend 3329
+#define wxStyledTextCtrl_LineEndRectExtend 3330
+#define wxStyledTextCtrl_PageUpRectExtend 3331
+#define wxStyledTextCtrl_PageDownRectExtend 3332
+#define wxStyledTextCtrl_StutteredPageUp 3333
+#define wxStyledTextCtrl_StutteredPageUpExtend 3334
+#define wxStyledTextCtrl_StutteredPageDown 3335
+#define wxStyledTextCtrl_StutteredPageDownExtend 3336
+#define wxStyledTextCtrl_WordLeftEnd 3337
+#define wxStyledTextCtrl_WordLeftEndExtend 3338
+#define wxStyledTextCtrl_WordRightEnd 3339
+#define wxStyledTextCtrl_WordRightEndExtend 3340
+#define wxStyledTextCtrl_SetWhitespaceChars 3341
+#define wxStyledTextCtrl_SetCharsDefault 3342
+#define wxStyledTextCtrl_AutoCompGetCurrent 3343
+#define wxStyledTextCtrl_Allocate 3344
+#define wxStyledTextCtrl_FindColumn 3345
+#define wxStyledTextCtrl_GetCaretSticky 3346
+#define wxStyledTextCtrl_SetCaretSticky 3347
+#define wxStyledTextCtrl_ToggleCaretSticky 3348
+#define wxStyledTextCtrl_SetPasteConvertEndings 3349
+#define wxStyledTextCtrl_GetPasteConvertEndings 3350
+#define wxStyledTextCtrl_SelectionDuplicate 3351
+#define wxStyledTextCtrl_SetCaretLineBackAlpha 3352
+#define wxStyledTextCtrl_GetCaretLineBackAlpha 3353
+#define wxStyledTextCtrl_StartRecord 3354
+#define wxStyledTextCtrl_StopRecord 3355
+#define wxStyledTextCtrl_SetLexer 3356
+#define wxStyledTextCtrl_GetLexer 3357
+#define wxStyledTextCtrl_Colourise 3358
+#define wxStyledTextCtrl_SetProperty 3359
+#define wxStyledTextCtrl_SetKeyWords 3360
+#define wxStyledTextCtrl_SetLexerLanguage 3361
+#define wxStyledTextCtrl_GetProperty 3362
+#define wxStyledTextCtrl_GetStyleBitsNeeded 3363
+#define wxStyledTextCtrl_GetCurrentLine 3364
+#define wxStyledTextCtrl_StyleSetSpec 3365
+#define wxStyledTextCtrl_StyleSetFont 3366
+#define wxStyledTextCtrl_StyleSetFontAttr 3367
+#define wxStyledTextCtrl_StyleSetCharacterSet 3368
+#define wxStyledTextCtrl_StyleSetFontEncoding 3369
+#define wxStyledTextCtrl_CmdKeyExecute 3370
+#define wxStyledTextCtrl_SetMargins 3371
+#define wxStyledTextCtrl_GetSelection 3372
+#define wxStyledTextCtrl_PointFromPosition 3373
+#define wxStyledTextCtrl_ScrollToLine 3374
+#define wxStyledTextCtrl_ScrollToColumn 3375
+#define wxStyledTextCtrl_SetVScrollBar 3376
+#define wxStyledTextCtrl_SetHScrollBar 3377
+#define wxStyledTextCtrl_GetLastKeydownProcessed 3378
+#define wxStyledTextCtrl_SetLastKeydownProcessed 3379
+#define wxStyledTextCtrl_SaveFile 3380
+#define wxStyledTextCtrl_LoadFile 3381
+#define wxStyledTextCtrl_DoDragOver 3382
+#define wxStyledTextCtrl_DoDropText 3383
+#define wxStyledTextCtrl_GetUseAntiAliasing 3384
+#define wxStyledTextCtrl_AddTextRaw 3385
+#define wxStyledTextCtrl_InsertTextRaw 3386
+#define wxStyledTextCtrl_GetCurLineRaw 3387
+#define wxStyledTextCtrl_GetLineRaw 3388
+#define wxStyledTextCtrl_GetSelectedTextRaw 3389
+#define wxStyledTextCtrl_GetTextRangeRaw 3390
+#define wxStyledTextCtrl_SetTextRaw 3391
+#define wxStyledTextCtrl_GetTextRaw 3392
+#define wxStyledTextCtrl_AppendTextRaw 3393
+#define wxArtProvider_GetBitmap 3394
+#define wxArtProvider_GetIcon 3395
+#define wxTreeEvent_GetKeyCode 3396
+#define wxTreeEvent_GetItem 3397
+#define wxTreeEvent_GetKeyEvent 3398
+#define wxTreeEvent_GetLabel 3399
+#define wxTreeEvent_GetOldItem 3400
+#define wxTreeEvent_GetPoint 3401
+#define wxTreeEvent_IsEditCancelled 3402
+#define wxTreeEvent_SetToolTip 3403
+#define wxNotebookEvent_GetOldSelection 3404
+#define wxNotebookEvent_GetSelection 3405
+#define wxNotebookEvent_SetOldSelection 3406
+#define wxNotebookEvent_SetSelection 3407
+#define wxFileDataObject_new 3408
+#define wxFileDataObject_AddFile 3409
+#define wxFileDataObject_GetFilenames 3410
+#define wxFileDataObject_destroy 3411
+#define wxTextDataObject_new 3412
+#define wxTextDataObject_GetTextLength 3413
+#define wxTextDataObject_GetText 3414
+#define wxTextDataObject_SetText 3415
+#define wxTextDataObject_destroy 3416
+#define wxBitmapDataObject_new_1_1 3417
+#define wxBitmapDataObject_new_1_0 3418
+#define wxBitmapDataObject_GetBitmap 3419
+#define wxBitmapDataObject_SetBitmap 3420
+#define wxBitmapDataObject_destroy 3421
+#define wxClipboard_new 3423
+#define wxClipboard_destruct 3424
+#define wxClipboard_AddData 3425
+#define wxClipboard_Clear 3426
+#define wxClipboard_Close 3427
+#define wxClipboard_Flush 3428
+#define wxClipboard_GetData 3429
+#define wxClipboard_IsOpened 3430
+#define wxClipboard_Open 3431
+#define wxClipboard_SetData 3432
+#define wxClipboard_UsePrimarySelection 3434
+#define wxClipboard_IsSupported 3435
+#define wxClipboard_Get 3436
+#define wxSpinEvent_GetPosition 3437
+#define wxSpinEvent_SetPosition 3438
+#define wxSplitterWindow_new_0 3439
+#define wxSplitterWindow_new_2 3440
+#define wxSplitterWindow_destruct 3441
+#define wxSplitterWindow_Create 3442
+#define wxSplitterWindow_GetMinimumPaneSize 3443
+#define wxSplitterWindow_GetSashGravity 3444
+#define wxSplitterWindow_GetSashPosition 3445
+#define wxSplitterWindow_GetSplitMode 3446
+#define wxSplitterWindow_GetWindow1 3447
+#define wxSplitterWindow_GetWindow2 3448
+#define wxSplitterWindow_Initialize 3449
+#define wxSplitterWindow_IsSplit 3450
+#define wxSplitterWindow_ReplaceWindow 3451
+#define wxSplitterWindow_SetSashGravity 3452
+#define wxSplitterWindow_SetSashPosition 3453
+#define wxSplitterWindow_SetSashSize 3454
+#define wxSplitterWindow_SetMinimumPaneSize 3455
+#define wxSplitterWindow_SetSplitMode 3456
+#define wxSplitterWindow_SplitHorizontally 3457
+#define wxSplitterWindow_SplitVertically 3458
+#define wxSplitterWindow_Unsplit 3459
+#define wxSplitterWindow_UpdateSize 3460
+#define wxSplitterEvent_GetSashPosition 3461
+#define wxSplitterEvent_GetX 3462
+#define wxSplitterEvent_GetY 3463
+#define wxSplitterEvent_GetWindowBeingRemoved 3464
+#define wxSplitterEvent_SetSashPosition 3465
+#define wxHtmlWindow_new_0 3466
+#define wxHtmlWindow_new_2 3467
+#define wxHtmlWindow_AppendToPage 3468
+#define wxHtmlWindow_GetOpenedAnchor 3469
+#define wxHtmlWindow_GetOpenedPage 3470
+#define wxHtmlWindow_GetOpenedPageTitle 3471
+#define wxHtmlWindow_GetRelatedFrame 3472
+#define wxHtmlWindow_HistoryBack 3473
+#define wxHtmlWindow_HistoryCanBack 3474
+#define wxHtmlWindow_HistoryCanForward 3475
+#define wxHtmlWindow_HistoryClear 3476
+#define wxHtmlWindow_HistoryForward 3477
+#define wxHtmlWindow_LoadFile 3478
+#define wxHtmlWindow_LoadPage 3479
+#define wxHtmlWindow_SelectAll 3480
+#define wxHtmlWindow_SelectionToText 3481
+#define wxHtmlWindow_SelectLine 3482
+#define wxHtmlWindow_SelectWord 3483
+#define wxHtmlWindow_SetBorders 3484
+#define wxHtmlWindow_SetFonts 3485
+#define wxHtmlWindow_SetPage 3486
+#define wxHtmlWindow_SetRelatedFrame 3487
+#define wxHtmlWindow_SetRelatedStatusBar 3488
+#define wxHtmlWindow_ToText 3489
+#define wxHtmlWindow_destroy 3490
+#define wxHtmlLinkEvent_GetLinkInfo 3491
+#define wxSystemSettings_GetColour 3492
+#define wxSystemSettings_GetFont 3493
+#define wxSystemSettings_GetMetric 3494
+#define wxSystemSettings_GetScreenType 3495
+#define wxSystemOptions_GetOption 3496
+#define wxSystemOptions_GetOptionInt 3497
+#define wxSystemOptions_HasOption 3498
+#define wxSystemOptions_IsFalse 3499
+#define wxSystemOptions_SetOption_2_1 3500
+#define wxSystemOptions_SetOption_2_0 3501
+#define wxAuiNotebookEvent_SetSelection 3502
+#define wxAuiNotebookEvent_GetSelection 3503
+#define wxAuiNotebookEvent_SetOldSelection 3504
+#define wxAuiNotebookEvent_GetOldSelection 3505
+#define wxAuiNotebookEvent_SetDragSource 3506
+#define wxAuiNotebookEvent_GetDragSource 3507
+#define wxAuiManagerEvent_SetManager 3508
+#define wxAuiManagerEvent_GetManager 3509
+#define wxAuiManagerEvent_SetPane 3510
+#define wxAuiManagerEvent_GetPane 3511
+#define wxAuiManagerEvent_SetButton 3512
+#define wxAuiManagerEvent_GetButton 3513
+#define wxAuiManagerEvent_SetDC 3514
+#define wxAuiManagerEvent_GetDC 3515
+#define wxAuiManagerEvent_Veto 3516
+#define wxAuiManagerEvent_GetVeto 3517
+#define wxAuiManagerEvent_SetCanVeto 3518
+#define wxAuiManagerEvent_CanVeto 3519
+#define wxLogNull_new 3520
+#define wxLogNull_destroy 3521
+#define wxTaskBarIcon_new 3522
+#define wxTaskBarIcon_destruct 3523
+#define wxTaskBarIcon_PopupMenu 3524
+#define wxTaskBarIcon_RemoveIcon 3525
+#define wxTaskBarIcon_SetIcon 3526
+#define wxLocale_new_0 3527
+#define wxLocale_new_2 3529
+#define wxLocale_destruct 3530
+#define wxLocale_Init 3532
+#define wxLocale_AddCatalog_1 3533
+#define wxLocale_AddCatalog_3 3534
+#define wxLocale_AddCatalogLookupPathPrefix 3535
+#define wxLocale_GetCanonicalName 3536
+#define wxLocale_GetLanguage 3537
+#define wxLocale_GetLanguageName 3538
+#define wxLocale_GetLocale 3539
+#define wxLocale_GetName 3540
+#define wxLocale_GetString_2 3541
+#define wxLocale_GetString_4 3542
+#define wxLocale_GetHeaderValue 3543
+#define wxLocale_GetSysName 3544
+#define wxLocale_GetSystemEncoding 3545
+#define wxLocale_GetSystemEncodingName 3546
+#define wxLocale_GetSystemLanguage 3547
+#define wxLocale_IsLoaded 3548
+#define wxLocale_IsOk 3549
diff --git a/lib/wx/c_src/wxe_callback_impl.cpp b/lib/wx/c_src/wxe_callback_impl.cpp
index e06f68dcbf..3a59004eb0 100644
--- a/lib/wx/c_src/wxe_callback_impl.cpp
+++ b/lib/wx/c_src/wxe_callback_impl.cpp
@@ -284,7 +284,7 @@ EwxListCtrl::~EwxListCtrl() {
* wxListCtrlCompare wrapper
* ****************************************************************************/
-int wxCALLBACK wxEListCtrlCompare(long item1, long item2, long callbackInfoPtr)
+int wxCALLBACK wxEListCtrlCompare(wxeIntPtr item1, wxeIntPtr item2, wxeIntPtr callbackInfoPtr)
{
callbackInfo * cb = (callbackInfo *)callbackInfoPtr;
wxeMemEnv * memenv = ((WxeApp *) wxTheApp)->getMemEnv(cb->port);
diff --git a/lib/wx/c_src/wxe_callback_impl.h b/lib/wx/c_src/wxe_callback_impl.h
index 1c355e4d38..ecfcd3db41 100644
--- a/lib/wx/c_src/wxe_callback_impl.h
+++ b/lib/wx/c_src/wxe_callback_impl.h
@@ -23,6 +23,13 @@
void pre_callback();
void handle_event_callback(ErlDrvPort port, ErlDrvTermData process);
+#if wxCHECK_VERSION(2,9,0)
+ #define wxeIntPtr wxIntPtr
+#else
+ // This is bad but how it was in wx-2.8
+ #define wxeIntPtr long
+#endif
+
/* Fun Callback id */
class wxeEvtListener : public wxEvtHandler
{
@@ -88,6 +95,6 @@ struct callbackInfo {
int callbackID;
};
-int wxCALLBACK wxEListCtrlCompare(long item1, long item2, long callbackInfoPtr);
+int wxCALLBACK wxEListCtrlCompare(wxeIntPtr item1, wxeIntPtr item2, wxeIntPtr callbackInfoPtr);
#endif
diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp
index 5964ccfd00..f617aaf349 100644
--- a/lib/wx/c_src/wxe_impl.cpp
+++ b/lib/wx/c_src/wxe_impl.cpp
@@ -104,7 +104,7 @@ void meta_command(int what, wxe_data *sd) {
}
}
-void send_msg(const char * type, wxString * msg) {
+void send_msg(const char * type, const wxString * msg) {
wxeReturn rt = wxeReturn(WXE_DRV_PORT, init_caller);
rt.addAtom((char *) "wxe_driver");
rt.addAtom((char *) type);
@@ -160,6 +160,13 @@ bool WxeApp::OnInit()
return TRUE;
}
+
+#ifdef _MACOSX
+void WxeApp::MacOpenFile(const wxString &filename) {
+ send_msg("open_file", &filename);
+}
+#endif
+
void WxeApp::shutdown(wxeMetaCommand& Ecmd) {
ExitMainLoop();
}
@@ -411,6 +418,13 @@ void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd)
wxWindow *parent = NULL;
wxeMemEnv * memenv = refmap[Ecmd.port];
+ if(!memenv) {
+ wxString msg;
+ msg.Printf(wxT("MemEnv already deleted"));
+ send_msg("debug", &msg);
+ return;
+ }
+
if(wxe_debug) {
wxString msg;
msg.Printf(wxT("Destroying all memory "));
@@ -439,7 +453,6 @@ void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd)
}
if(recurse_level > 0) {
// Delay delete until we are out of dispatch*
- delayed_cleanup->Append(Ecmd.Clone());
} else {
delete win;
}
@@ -448,9 +461,10 @@ void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd)
}
}
- if(recurse_level > 0)
+ if(recurse_level > 0) {
+ delayed_cleanup->Append(Ecmd.Clone());
return;
-
+ }
// First pass, delete all top parents/windows of all linked objects
// fprintf(stderr, "close port %x\r\n", Ecmd.port);fflush(stderr);
@@ -486,20 +500,20 @@ void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd)
if(it != ptr2ref.end()) {
wxeRefData *refd = it->second;
if(refd->alloc_in_erl) {
- int type = refd->type;
if((refd->type == 1) && ((wxObject *)ptr)->IsKindOf(CLASSINFO(wxBufferedDC))) {
((wxBufferedDC *)ptr)->m_dc = NULL; // Workaround
}
wxString msg;
- if((refd->type == 0)) { // Maybe also class 1
+ bool cleanup_ref=true;
+ if(refd->type == 0) { // Maybe also class 1
wxClassInfo *cinfo = ((wxObject *)ptr)->GetClassInfo();
msg.Printf(wxT("Memory leak: {wx_ref, %d, %s}"),
refd->ref, cinfo->GetClassName());
send_msg("error", &msg);
} else {
- delete_object(ptr, refd);
+ cleanup_ref = delete_object(ptr, refd);
}
- if(type == 0 || type > 2) {
+ if(cleanup_ref) {
// Delete refs for leaks and non overridden allocs
delete refd;
ptr2ref.erase(it);
@@ -607,7 +621,7 @@ void WxeApp::clearPtr(void * ptr) {
if(((int) refd->pid) != -1) {
// Send terminate pid to owner
- wxeReturn rt = wxeReturn(WXE_DRV_PORT,refd->memenv->owner, false);
+ wxeReturn rt = wxeReturn(WXE_DRV_PORT,refd->pid, false);
rt.addAtom("_wxe_destroy_");
rt.add(ERL_DRV_PID, refd->pid);
rt.addTupleCount(2);
diff --git a/lib/wx/c_src/wxe_impl.h b/lib/wx/c_src/wxe_impl.h
index bb54961edd..5b23e1cbbd 100644
--- a/lib/wx/c_src/wxe_impl.h
+++ b/lib/wx/c_src/wxe_impl.h
@@ -36,17 +36,28 @@ extern "C" {
#include "wxe_callback_impl.h"
#include "wxe_memory.h"
+#if !wxCHECK_VERSION(2,9,0)
+#define wxeLocaleC wxChar *
+#define wxeLocaleC2String(Str) wxString(Str)
+#else
+typedef wxString wxeLocaleC;
+#define wxeLocaleC2String(Str) Str
+#endif
+
#define WXE_NOT_INITIATED 0
#define WXE_INITIATED 1
#define WXE_EXITED 2
#define WXE_ERROR -1
-void send_msg(const char *, wxString *); // For debugging and error msgs
+void send_msg(const char *, const wxString *); // For debugging and error msgs
class WxeApp : public wxApp
{
public:
- virtual bool OnInit();
+ virtual bool OnInit();
+#ifdef _MACOSX
+ virtual void MacOpenFile(const wxString &filename);
+#endif
void shutdown(wxeMetaCommand& event);
int dispatch(wxList *, int, int);
@@ -73,7 +84,7 @@ public:
void init_nonconsts(wxeMemEnv *memenv, ErlDrvTermData caller);
// Code found in gen/wxe_derived_dest.h
- void delete_object(void *ptr, wxeRefData *refd);
+ bool delete_object(void *ptr, wxeRefData *refd);
wxeMemMap refmap;
ptrMap ptr2ref;
diff --git a/lib/wx/configure.in b/lib/wx/configure.in
index 3756786831..a96f1f2632 100755
--- a/lib/wx/configure.in
+++ b/lib/wx/configure.in
@@ -441,12 +441,12 @@ else
else
CWXWIN_PROG=`cygpath -d "$PROGRAMFILES" | cygpath -f - 2>/dev/null`
fi
- CWXWIN3=$CWXWIN_PROG/wxWidgets-2.*.*
- CWXWIN4=$CWXWIN_PROG/wxMSW-2.*.*
- CWX_DOCUMENTED="/opt/local/pgm/wxMSW-2.*.* /opt/local/pgm/wxWidgets-2.*.*"
+ CWXWIN3=$CWXWIN_PROG/wxWidgets-?.*.*
+ CWXWIN4=$CWXWIN_PROG/wxMSW-?.*.*
+ CWX_DOCUMENTED="/opt/local/pgm/wxMSW-?.*.* /opt/local/pgm/wxWidgets-?.*.*"
case $ac_cv_sizeof_void_p in
8)
- CWX_DOCUMENTED="/opt/local64/pgm/wxMSW-2.*.* /opt/local64/pgm/wxWidgets-2.*.* $CWX_DOCUMENTED"
+ CWX_DOCUMENTED="/opt/local64/pgm/wxMSW-?.*.* /opt/local64/pgm/wxWidgets-?.*.* $CWX_DOCUMENTED"
;;
*)
true
@@ -464,12 +464,14 @@ else
WX_CFLAGS="-EHsc -D_UNICODE -DUNICODE -I$WXINCLUDE_MSVC -I$WXINCLUDE_PLAIN -I$WXINCLUDE_CONTRIB -D__WXMSW__"
WX_CXXFLAGS="-TP $WX_CFLAGS"
WX_LIBDIR=$dir/lib/vc_lib
+ WX_LIBDIR64=$dir/lib/vc_x64_lib
WX_RESCOMP="rc.sh -I$WXINCLUDE_PLAIN -D __WIN32__"
RC_FILE_TYPE=res
- for lib in $WX_LIBDIR/wxbase*.lib $WX_LIBDIR2/wxbase*.lib; do
- maybe=`echo $lib | egrep 'wxbase[[0-9]]*u\.lib'`
+ for lib in $WX_LIBDIR $WX_LIBDIR64; do
+ maybe=`ls $lib/wxbase*.lib | egrep 'wxbase[[0-9]]*u\.lib'`
if test '!' -z "$maybe"; then
corelib_number=`echo $maybe | sed 's,.*\([[0-9]].\)u\.lib,\1,'`
+ WX_LIBDIR=$lib
break
fi
done
diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml
index 6e653cf828..daa61fda1e 100644
--- a/lib/wx/doc/src/notes.xml
+++ b/lib/wx/doc/src/notes.xml
@@ -31,6 +31,59 @@
<p>This document describes the changes made to the wxErlang
application.</p>
+<section><title>Wx 1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Refactored C++ code, fixed crashes and a deadlock on
+ linux.</p>
+ <p>
+ Own Id: OTP-11586</p>
+ </item>
+ <item>
+ <p>
+ Some local implementations of removing the last element
+ from a list are replaced by <c>lists:droplast/1</c>. Note
+ that this requires at least <c>stdlib-2.0</c>, which is
+ the stdlib version delivered in OTP 17.0. (Thanks to Hans
+ Svensson)</p>
+ <p>
+ Own Id: OTP-11678</p>
+ </item>
+ <item>
+ <p>
+ Reworked the internal event handling to avoid crashes in
+ destroy objects. Thanks Tom for the bug report.</p>
+ <p>
+ Own Id: OTP-11699</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Wx 1.1.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl
index 2dc1791cce..ac852ce054 100644
--- a/lib/wx/include/wx.hrl
+++ b/lib/wx/include/wx.hrl
@@ -1980,10 +1980,255 @@
-define(wxIMAGELIST_DRAW_SELECTED, 4).
-define(wxIMAGELIST_DRAW_TRANSPARENT, 2).
-define(wxIMAGELIST_DRAW_NORMAL, 1).
+% From "intl.h": wxLanguage
+-define(wxLANGUAGE_DEFAULT, 0).
+-define(wxLANGUAGE_UNKNOWN, 1).
+-define(wxLANGUAGE_ABKHAZIAN, 2).
+-define(wxLANGUAGE_AFAR, 3).
+-define(wxLANGUAGE_AFRIKAANS, 4).
+-define(wxLANGUAGE_ALBANIAN, 5).
+-define(wxLANGUAGE_AMHARIC, 6).
+-define(wxLANGUAGE_ARABIC, 7).
+-define(wxLANGUAGE_ARABIC_ALGERIA, 8).
+-define(wxLANGUAGE_ARABIC_BAHRAIN, 9).
+-define(wxLANGUAGE_ARABIC_EGYPT, 10).
+-define(wxLANGUAGE_ARABIC_IRAQ, 11).
+-define(wxLANGUAGE_ARABIC_JORDAN, 12).
+-define(wxLANGUAGE_ARABIC_KUWAIT, 13).
+-define(wxLANGUAGE_ARABIC_LEBANON, 14).
+-define(wxLANGUAGE_ARABIC_LIBYA, 15).
+-define(wxLANGUAGE_ARABIC_MOROCCO, 16).
+-define(wxLANGUAGE_ARABIC_OMAN, 17).
+-define(wxLANGUAGE_ARABIC_QATAR, 18).
+-define(wxLANGUAGE_ARABIC_SAUDI_ARABIA, 19).
+-define(wxLANGUAGE_ARABIC_SUDAN, 20).
+-define(wxLANGUAGE_ARABIC_SYRIA, 21).
+-define(wxLANGUAGE_ARABIC_TUNISIA, 22).
+-define(wxLANGUAGE_ARABIC_UAE, 23).
+-define(wxLANGUAGE_ARABIC_YEMEN, 24).
+-define(wxLANGUAGE_ARMENIAN, 25).
+-define(wxLANGUAGE_ASSAMESE, 26).
+-define(wxLANGUAGE_AYMARA, 27).
+-define(wxLANGUAGE_AZERI, 28).
+-define(wxLANGUAGE_AZERI_CYRILLIC, 29).
+-define(wxLANGUAGE_AZERI_LATIN, 30).
+-define(wxLANGUAGE_BASHKIR, 31).
+-define(wxLANGUAGE_BASQUE, 32).
+-define(wxLANGUAGE_BELARUSIAN, 33).
+-define(wxLANGUAGE_BENGALI, 34).
+-define(wxLANGUAGE_BHUTANI, 35).
+-define(wxLANGUAGE_BIHARI, 36).
+-define(wxLANGUAGE_BISLAMA, 37).
+-define(wxLANGUAGE_BRETON, 38).
+-define(wxLANGUAGE_BULGARIAN, 39).
+-define(wxLANGUAGE_BURMESE, 40).
+-define(wxLANGUAGE_CAMBODIAN, 41).
+-define(wxLANGUAGE_CATALAN, 42).
+-define(wxLANGUAGE_CHINESE, 43).
+-define(wxLANGUAGE_CHINESE_SIMPLIFIED, 44).
+-define(wxLANGUAGE_CHINESE_TRADITIONAL, 45).
+-define(wxLANGUAGE_CHINESE_HONGKONG, 46).
+-define(wxLANGUAGE_CHINESE_MACAU, 47).
+-define(wxLANGUAGE_CHINESE_SINGAPORE, 48).
+-define(wxLANGUAGE_CHINESE_TAIWAN, 49).
+-define(wxLANGUAGE_CORSICAN, 50).
+-define(wxLANGUAGE_CROATIAN, 51).
+-define(wxLANGUAGE_CZECH, 52).
+-define(wxLANGUAGE_DANISH, 53).
+-define(wxLANGUAGE_DUTCH, 54).
+-define(wxLANGUAGE_DUTCH_BELGIAN, 55).
+-define(wxLANGUAGE_ENGLISH, 56).
+-define(wxLANGUAGE_ENGLISH_UK, 57).
+-define(wxLANGUAGE_ENGLISH_US, 58).
+-define(wxLANGUAGE_ENGLISH_AUSTRALIA, 59).
+-define(wxLANGUAGE_ENGLISH_BELIZE, 60).
+-define(wxLANGUAGE_ENGLISH_BOTSWANA, 61).
+-define(wxLANGUAGE_ENGLISH_CANADA, 62).
+-define(wxLANGUAGE_ENGLISH_CARIBBEAN, 63).
+-define(wxLANGUAGE_ENGLISH_DENMARK, 64).
+-define(wxLANGUAGE_ENGLISH_EIRE, 65).
+-define(wxLANGUAGE_ENGLISH_JAMAICA, 66).
+-define(wxLANGUAGE_ENGLISH_NEW_ZEALAND, 67).
+-define(wxLANGUAGE_ENGLISH_PHILIPPINES, 68).
+-define(wxLANGUAGE_ENGLISH_SOUTH_AFRICA, 69).
+-define(wxLANGUAGE_ENGLISH_TRINIDAD, 70).
+-define(wxLANGUAGE_ENGLISH_ZIMBABWE, 71).
+-define(wxLANGUAGE_ESPERANTO, 72).
+-define(wxLANGUAGE_ESTONIAN, 73).
+-define(wxLANGUAGE_FAEROESE, 74).
+-define(wxLANGUAGE_FARSI, 75).
+-define(wxLANGUAGE_FIJI, 76).
+-define(wxLANGUAGE_FINNISH, 77).
+-define(wxLANGUAGE_FRENCH, 78).
+-define(wxLANGUAGE_FRENCH_BELGIAN, 79).
+-define(wxLANGUAGE_FRENCH_CANADIAN, 80).
+-define(wxLANGUAGE_FRENCH_LUXEMBOURG, 81).
+-define(wxLANGUAGE_FRENCH_MONACO, 82).
+-define(wxLANGUAGE_FRENCH_SWISS, 83).
+-define(wxLANGUAGE_FRISIAN, 84).
+-define(wxLANGUAGE_GALICIAN, 85).
+-define(wxLANGUAGE_GEORGIAN, 86).
+-define(wxLANGUAGE_GERMAN, 87).
+-define(wxLANGUAGE_GERMAN_AUSTRIAN, 88).
+-define(wxLANGUAGE_GERMAN_BELGIUM, 89).
+-define(wxLANGUAGE_GERMAN_LIECHTENSTEIN, 90).
+-define(wxLANGUAGE_GERMAN_LUXEMBOURG, 91).
+-define(wxLANGUAGE_GERMAN_SWISS, 92).
+-define(wxLANGUAGE_GREEK, 93).
+-define(wxLANGUAGE_GREENLANDIC, 94).
+-define(wxLANGUAGE_GUARANI, 95).
+-define(wxLANGUAGE_GUJARATI, 96).
+-define(wxLANGUAGE_HAUSA, 97).
+-define(wxLANGUAGE_HEBREW, 98).
+-define(wxLANGUAGE_HINDI, 99).
+-define(wxLANGUAGE_HUNGARIAN, 100).
+-define(wxLANGUAGE_ICELANDIC, 101).
+-define(wxLANGUAGE_INDONESIAN, 102).
+-define(wxLANGUAGE_INTERLINGUA, 103).
+-define(wxLANGUAGE_INTERLINGUE, 104).
+-define(wxLANGUAGE_INUKTITUT, 105).
+-define(wxLANGUAGE_INUPIAK, 106).
+-define(wxLANGUAGE_IRISH, 107).
+-define(wxLANGUAGE_ITALIAN, 108).
+-define(wxLANGUAGE_ITALIAN_SWISS, 109).
+-define(wxLANGUAGE_JAPANESE, 110).
+-define(wxLANGUAGE_JAVANESE, 111).
+-define(wxLANGUAGE_KANNADA, 112).
+-define(wxLANGUAGE_KASHMIRI, 113).
+-define(wxLANGUAGE_KASHMIRI_INDIA, 114).
+-define(wxLANGUAGE_KAZAKH, 115).
+-define(wxLANGUAGE_KERNEWEK, 116).
+-define(wxLANGUAGE_KINYARWANDA, 117).
+-define(wxLANGUAGE_KIRGHIZ, 118).
+-define(wxLANGUAGE_KIRUNDI, 119).
+-define(wxLANGUAGE_KONKANI, 120).
+-define(wxLANGUAGE_KOREAN, 121).
+-define(wxLANGUAGE_KURDISH, 122).
+-define(wxLANGUAGE_LAOTHIAN, 123).
+-define(wxLANGUAGE_LATIN, 124).
+-define(wxLANGUAGE_LATVIAN, 125).
+-define(wxLANGUAGE_LINGALA, 126).
+-define(wxLANGUAGE_LITHUANIAN, 127).
+-define(wxLANGUAGE_MACEDONIAN, 128).
+-define(wxLANGUAGE_MALAGASY, 129).
+-define(wxLANGUAGE_MALAY, 130).
+-define(wxLANGUAGE_MALAYALAM, 131).
+-define(wxLANGUAGE_MALAY_BRUNEI_DARUSSALAM, 132).
+-define(wxLANGUAGE_MALAY_MALAYSIA, 133).
+-define(wxLANGUAGE_MALTESE, 134).
+-define(wxLANGUAGE_MANIPURI, 135).
+-define(wxLANGUAGE_MAORI, 136).
+-define(wxLANGUAGE_MARATHI, 137).
+-define(wxLANGUAGE_MOLDAVIAN, 138).
+-define(wxLANGUAGE_MONGOLIAN, 139).
+-define(wxLANGUAGE_NAURU, 140).
+-define(wxLANGUAGE_NEPALI, 141).
+-define(wxLANGUAGE_NEPALI_INDIA, 142).
+-define(wxLANGUAGE_NORWEGIAN_BOKMAL, 143).
+-define(wxLANGUAGE_NORWEGIAN_NYNORSK, 144).
+-define(wxLANGUAGE_OCCITAN, 145).
+-define(wxLANGUAGE_ORIYA, 146).
+-define(wxLANGUAGE_OROMO, 147).
+-define(wxLANGUAGE_PASHTO, 148).
+-define(wxLANGUAGE_POLISH, 149).
+-define(wxLANGUAGE_PORTUGUESE, 150).
+-define(wxLANGUAGE_PORTUGUESE_BRAZILIAN, 151).
+-define(wxLANGUAGE_PUNJABI, 152).
+-define(wxLANGUAGE_QUECHUA, 153).
+-define(wxLANGUAGE_RHAETO_ROMANCE, 154).
+-define(wxLANGUAGE_ROMANIAN, 155).
+-define(wxLANGUAGE_RUSSIAN, 156).
+-define(wxLANGUAGE_RUSSIAN_UKRAINE, 157).
+-define(wxLANGUAGE_SAMOAN, 158).
+-define(wxLANGUAGE_SANGHO, 159).
+-define(wxLANGUAGE_SANSKRIT, 160).
+-define(wxLANGUAGE_SCOTS_GAELIC, 161).
+-define(wxLANGUAGE_SERBIAN, 162).
+-define(wxLANGUAGE_SERBIAN_CYRILLIC, 163).
+-define(wxLANGUAGE_SERBIAN_LATIN, 164).
+-define(wxLANGUAGE_SERBO_CROATIAN, 165).
+-define(wxLANGUAGE_SESOTHO, 166).
+-define(wxLANGUAGE_SETSWANA, 167).
+-define(wxLANGUAGE_SHONA, 168).
+-define(wxLANGUAGE_SINDHI, 169).
+-define(wxLANGUAGE_SINHALESE, 170).
+-define(wxLANGUAGE_SISWATI, 171).
+-define(wxLANGUAGE_SLOVAK, 172).
+-define(wxLANGUAGE_SLOVENIAN, 173).
+-define(wxLANGUAGE_SOMALI, 174).
+-define(wxLANGUAGE_SPANISH, 175).
+-define(wxLANGUAGE_SPANISH_ARGENTINA, 176).
+-define(wxLANGUAGE_SPANISH_BOLIVIA, 177).
+-define(wxLANGUAGE_SPANISH_CHILE, 178).
+-define(wxLANGUAGE_SPANISH_COLOMBIA, 179).
+-define(wxLANGUAGE_SPANISH_COSTA_RICA, 180).
+-define(wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC, 181).
+-define(wxLANGUAGE_SPANISH_ECUADOR, 182).
+-define(wxLANGUAGE_SPANISH_EL_SALVADOR, 183).
+-define(wxLANGUAGE_SPANISH_GUATEMALA, 184).
+-define(wxLANGUAGE_SPANISH_HONDURAS, 185).
+-define(wxLANGUAGE_SPANISH_MEXICAN, 186).
+-define(wxLANGUAGE_SPANISH_MODERN, 187).
+-define(wxLANGUAGE_SPANISH_NICARAGUA, 188).
+-define(wxLANGUAGE_SPANISH_PANAMA, 189).
+-define(wxLANGUAGE_SPANISH_PARAGUAY, 190).
+-define(wxLANGUAGE_SPANISH_PERU, 191).
+-define(wxLANGUAGE_SPANISH_PUERTO_RICO, 192).
+-define(wxLANGUAGE_SPANISH_URUGUAY, 193).
+-define(wxLANGUAGE_SPANISH_US, 194).
+-define(wxLANGUAGE_SPANISH_VENEZUELA, 195).
+-define(wxLANGUAGE_SUNDANESE, 196).
+-define(wxLANGUAGE_SWAHILI, 197).
+-define(wxLANGUAGE_SWEDISH, 198).
+-define(wxLANGUAGE_SWEDISH_FINLAND, 199).
+-define(wxLANGUAGE_TAGALOG, 200).
+-define(wxLANGUAGE_TAJIK, 201).
+-define(wxLANGUAGE_TAMIL, 202).
+-define(wxLANGUAGE_TATAR, 203).
+-define(wxLANGUAGE_TELUGU, 204).
+-define(wxLANGUAGE_THAI, 205).
+-define(wxLANGUAGE_TIBETAN, 206).
+-define(wxLANGUAGE_TIGRINYA, 207).
+-define(wxLANGUAGE_TONGA, 208).
+-define(wxLANGUAGE_TSONGA, 209).
+-define(wxLANGUAGE_TURKISH, 210).
+-define(wxLANGUAGE_TURKMEN, 211).
+-define(wxLANGUAGE_TWI, 212).
+-define(wxLANGUAGE_UIGHUR, 213).
+-define(wxLANGUAGE_UKRAINIAN, 214).
+-define(wxLANGUAGE_URDU, 215).
+-define(wxLANGUAGE_URDU_INDIA, 216).
+-define(wxLANGUAGE_URDU_PAKISTAN, 217).
+-define(wxLANGUAGE_UZBEK, 218).
+-define(wxLANGUAGE_UZBEK_CYRILLIC, 219).
+-define(wxLANGUAGE_UZBEK_LATIN, 220).
+-define(wxLANGUAGE_VIETNAMESE, 221).
+-define(wxLANGUAGE_VOLAPUK, 222).
+-define(wxLANGUAGE_WELSH, 223).
+-define(wxLANGUAGE_WOLOF, 224).
+-define(wxLANGUAGE_XHOSA, 225).
+-define(wxLANGUAGE_YIDDISH, 226).
+-define(wxLANGUAGE_YORUBA, 227).
+-define(wxLANGUAGE_ZHUANG, 228).
+-define(wxLANGUAGE_ZULU, 229).
+-define(wxLANGUAGE_USER_DEFINED, 230).
+-define(wxLANGUAGE_VALENCIAN, 536870911).
+-define(wxLANGUAGE_SAMI, 536870912).
% From "intl.h": wxLayoutDirection
-define(wxLayout_Default, 0).
-define(wxLayout_LeftToRight, 1).
-define(wxLayout_RightToLeft, 2).
+% From "intl.h": wxLocaleCategory
+-define(wxLOCALE_CAT_NUMBER, 0).
+-define(wxLOCALE_CAT_DATE, 1).
+-define(wxLOCALE_CAT_MONEY, 2).
+-define(wxLOCALE_CAT_MAX, 3).
+% From "intl.h": wxLocaleInfo
+-define(wxLOCALE_THOUSANDS_SEP, 0).
+-define(wxLOCALE_DECIMAL_POINT, 1).
+% From "intl.h": wxLocaleInitFlags
+-define(wxLOCALE_LOAD_DEFAULT, 1).
+-define(wxLOCALE_CONV_ENCODING, 2).
% From "layout.h"
-define(wxLAYOUT_DEFAULT_MARGIN, 0).
% From "layout.h": wxEdge
diff --git a/lib/wx/src/gen/wxLocale.erl b/lib/wx/src/gen/wxLocale.erl
new file mode 100644
index 0000000000..a1a418bcee
--- /dev/null
+++ b/lib/wx/src/gen/wxLocale.erl
@@ -0,0 +1,278 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%% This file is generated DO NOT EDIT
+
+%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html">wxLocale</a>.
+%% @type wxLocale(). An object reference, The representation is internal
+%% and can be changed without notice. It can't be used for comparsion
+%% stored on disc or distributed for use on other nodes.
+
+-module(wxLocale).
+-include("wxe.hrl").
+-export([addCatalog/2,addCatalog/4,addCatalogLookupPathPrefix/1,destroy/1,
+ getCanonicalName/1,getHeaderValue/2,getHeaderValue/3,getLanguage/1,
+ getLanguageName/1,getLocale/1,getName/1,getString/2,getString/3,getString/4,
+ getString/5,getSysName/1,getSystemEncoding/0,getSystemEncodingName/0,
+ getSystemLanguage/0,init/1,init/2,isLoaded/2,isOk/1,new/0,new/1,new/2]).
+
+%% inherited exports
+-export([parent_class/1]).
+
+-export_type([wxLocale/0]).
+%% @hidden
+parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
+
+-type wxLocale() :: wx:wx_object().
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalewxlocale">external documentation</a>.
+-spec new() -> wxLocale().
+new() ->
+ wxe_util:construct(?wxLocale_new_0,
+ <<>>).
+
+%% @equiv new(Language, [])
+-spec new(Language) -> wxLocale() when
+ Language::integer().
+
+new(Language)
+ when is_integer(Language) ->
+ new(Language, []).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalewxlocale">external documentation</a>.
+-spec new(Language, [Option]) -> wxLocale() when
+ Language::integer(),
+ Option :: {flags, integer()}.
+new(Language, Options)
+ when is_integer(Language),is_list(Options) ->
+ MOpts = fun({flags, Flags}, Acc) -> [<<1:32/?UI,Flags:32/?UI>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:construct(?wxLocale_new_2,
+ <<Language:32/?UI, 0:32,BinOpt/binary>>).
+
+%% @equiv init(This, [])
+-spec init(This) -> boolean() when
+ This::wxLocale().
+
+init(This)
+ when is_record(This, wx_ref) ->
+ init(This, []).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleinit">external documentation</a>.
+-spec init(This, [Option]) -> boolean() when
+ This::wxLocale(),
+ Option :: {language, integer()}
+ | {flags, integer()}.
+init(#wx_ref{type=ThisT,ref=ThisRef}, Options)
+ when is_list(Options) ->
+ ?CLASS(ThisT,wxLocale),
+ MOpts = fun({language, Language}, Acc) -> [<<1:32/?UI,Language:32/?UI>>|Acc];
+ ({flags, Flags}, Acc) -> [<<2:32/?UI,Flags:32/?UI>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:call(?wxLocale_Init,
+ <<ThisRef:32/?UI, 0:32,BinOpt/binary>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleaddcatalog">external documentation</a>.
+-spec addCatalog(This, SzDomain) -> boolean() when
+ This::wxLocale(), SzDomain::unicode:chardata().
+addCatalog(#wx_ref{type=ThisT,ref=ThisRef},SzDomain)
+ when is_list(SzDomain) ->
+ ?CLASS(ThisT,wxLocale),
+ SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),
+ wxe_util:call(?wxLocale_AddCatalog_1,
+ <<ThisRef:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleaddcatalog">external documentation</a>.
+%%<br /> MsgIdLanguage = ?wxLANGUAGE_DEFAULT | ?wxLANGUAGE_UNKNOWN | ?wxLANGUAGE_ABKHAZIAN | ?wxLANGUAGE_AFAR | ?wxLANGUAGE_AFRIKAANS | ?wxLANGUAGE_ALBANIAN | ?wxLANGUAGE_AMHARIC | ?wxLANGUAGE_ARABIC | ?wxLANGUAGE_ARABIC_ALGERIA | ?wxLANGUAGE_ARABIC_BAHRAIN | ?wxLANGUAGE_ARABIC_EGYPT | ?wxLANGUAGE_ARABIC_IRAQ | ?wxLANGUAGE_ARABIC_JORDAN | ?wxLANGUAGE_ARABIC_KUWAIT | ?wxLANGUAGE_ARABIC_LEBANON | ?wxLANGUAGE_ARABIC_LIBYA | ?wxLANGUAGE_ARABIC_MOROCCO | ?wxLANGUAGE_ARABIC_OMAN | ?wxLANGUAGE_ARABIC_QATAR | ?wxLANGUAGE_ARABIC_SAUDI_ARABIA | ?wxLANGUAGE_ARABIC_SUDAN | ?wxLANGUAGE_ARABIC_SYRIA | ?wxLANGUAGE_ARABIC_TUNISIA | ?wxLANGUAGE_ARABIC_UAE | ?wxLANGUAGE_ARABIC_YEMEN | ?wxLANGUAGE_ARMENIAN | ?wxLANGUAGE_ASSAMESE | ?wxLANGUAGE_AYMARA | ?wxLANGUAGE_AZERI | ?wxLANGUAGE_AZERI_CYRILLIC | ?wxLANGUAGE_AZERI_LATIN | ?wxLANGUAGE_BASHKIR | ?wxLANGUAGE_BASQUE | ?wxLANGUAGE_BELARUSIAN | ?wxLANGUAGE_BENGALI | ?wxLANGUAGE_BHUTANI | ?wxLANGUAGE_BIHARI | ?wxLANGUAGE_BISLAMA | ?wxLANGUAGE_BRETON | ?wxLANGUAGE_BULGARIAN | ?wxLANGUAGE_BURMESE | ?wxLANGUAGE_CAMBODIAN | ?wxLANGUAGE_CATALAN | ?wxLANGUAGE_CHINESE | ?wxLANGUAGE_CHINESE_SIMPLIFIED | ?wxLANGUAGE_CHINESE_TRADITIONAL | ?wxLANGUAGE_CHINESE_HONGKONG | ?wxLANGUAGE_CHINESE_MACAU | ?wxLANGUAGE_CHINESE_SINGAPORE | ?wxLANGUAGE_CHINESE_TAIWAN | ?wxLANGUAGE_CORSICAN | ?wxLANGUAGE_CROATIAN | ?wxLANGUAGE_CZECH | ?wxLANGUAGE_DANISH | ?wxLANGUAGE_DUTCH | ?wxLANGUAGE_DUTCH_BELGIAN | ?wxLANGUAGE_ENGLISH | ?wxLANGUAGE_ENGLISH_UK | ?wxLANGUAGE_ENGLISH_US | ?wxLANGUAGE_ENGLISH_AUSTRALIA | ?wxLANGUAGE_ENGLISH_BELIZE | ?wxLANGUAGE_ENGLISH_BOTSWANA | ?wxLANGUAGE_ENGLISH_CANADA | ?wxLANGUAGE_ENGLISH_CARIBBEAN | ?wxLANGUAGE_ENGLISH_DENMARK | ?wxLANGUAGE_ENGLISH_EIRE | ?wxLANGUAGE_ENGLISH_JAMAICA | ?wxLANGUAGE_ENGLISH_NEW_ZEALAND | ?wxLANGUAGE_ENGLISH_PHILIPPINES | ?wxLANGUAGE_ENGLISH_SOUTH_AFRICA | ?wxLANGUAGE_ENGLISH_TRINIDAD | ?wxLANGUAGE_ENGLISH_ZIMBABWE | ?wxLANGUAGE_ESPERANTO | ?wxLANGUAGE_ESTONIAN | ?wxLANGUAGE_FAEROESE | ?wxLANGUAGE_FARSI | ?wxLANGUAGE_FIJI | ?wxLANGUAGE_FINNISH | ?wxLANGUAGE_FRENCH | ?wxLANGUAGE_FRENCH_BELGIAN | ?wxLANGUAGE_FRENCH_CANADIAN | ?wxLANGUAGE_FRENCH_LUXEMBOURG | ?wxLANGUAGE_FRENCH_MONACO | ?wxLANGUAGE_FRENCH_SWISS | ?wxLANGUAGE_FRISIAN | ?wxLANGUAGE_GALICIAN | ?wxLANGUAGE_GEORGIAN | ?wxLANGUAGE_GERMAN | ?wxLANGUAGE_GERMAN_AUSTRIAN | ?wxLANGUAGE_GERMAN_BELGIUM | ?wxLANGUAGE_GERMAN_LIECHTENSTEIN | ?wxLANGUAGE_GERMAN_LUXEMBOURG | ?wxLANGUAGE_GERMAN_SWISS | ?wxLANGUAGE_GREEK | ?wxLANGUAGE_GREENLANDIC | ?wxLANGUAGE_GUARANI | ?wxLANGUAGE_GUJARATI | ?wxLANGUAGE_HAUSA | ?wxLANGUAGE_HEBREW | ?wxLANGUAGE_HINDI | ?wxLANGUAGE_HUNGARIAN | ?wxLANGUAGE_ICELANDIC | ?wxLANGUAGE_INDONESIAN | ?wxLANGUAGE_INTERLINGUA | ?wxLANGUAGE_INTERLINGUE | ?wxLANGUAGE_INUKTITUT | ?wxLANGUAGE_INUPIAK | ?wxLANGUAGE_IRISH | ?wxLANGUAGE_ITALIAN | ?wxLANGUAGE_ITALIAN_SWISS | ?wxLANGUAGE_JAPANESE | ?wxLANGUAGE_JAVANESE | ?wxLANGUAGE_KANNADA | ?wxLANGUAGE_KASHMIRI | ?wxLANGUAGE_KASHMIRI_INDIA | ?wxLANGUAGE_KAZAKH | ?wxLANGUAGE_KERNEWEK | ?wxLANGUAGE_KINYARWANDA | ?wxLANGUAGE_KIRGHIZ | ?wxLANGUAGE_KIRUNDI | ?wxLANGUAGE_KONKANI | ?wxLANGUAGE_KOREAN | ?wxLANGUAGE_KURDISH | ?wxLANGUAGE_LAOTHIAN | ?wxLANGUAGE_LATIN | ?wxLANGUAGE_LATVIAN | ?wxLANGUAGE_LINGALA | ?wxLANGUAGE_LITHUANIAN | ?wxLANGUAGE_MACEDONIAN | ?wxLANGUAGE_MALAGASY | ?wxLANGUAGE_MALAY | ?wxLANGUAGE_MALAYALAM | ?wxLANGUAGE_MALAY_BRUNEI_DARUSSALAM | ?wxLANGUAGE_MALAY_MALAYSIA | ?wxLANGUAGE_MALTESE | ?wxLANGUAGE_MANIPURI | ?wxLANGUAGE_MAORI | ?wxLANGUAGE_MARATHI | ?wxLANGUAGE_MOLDAVIAN | ?wxLANGUAGE_MONGOLIAN | ?wxLANGUAGE_NAURU | ?wxLANGUAGE_NEPALI | ?wxLANGUAGE_NEPALI_INDIA | ?wxLANGUAGE_NORWEGIAN_BOKMAL | ?wxLANGUAGE_NORWEGIAN_NYNORSK | ?wxLANGUAGE_OCCITAN | ?wxLANGUAGE_ORIYA | ?wxLANGUAGE_OROMO | ?wxLANGUAGE_PASHTO | ?wxLANGUAGE_POLISH | ?wxLANGUAGE_PORTUGUESE | ?wxLANGUAGE_PORTUGUESE_BRAZILIAN | ?wxLANGUAGE_PUNJABI | ?wxLANGUAGE_QUECHUA | ?wxLANGUAGE_RHAETO_ROMANCE | ?wxLANGUAGE_ROMANIAN | ?wxLANGUAGE_RUSSIAN | ?wxLANGUAGE_RUSSIAN_UKRAINE | ?wxLANGUAGE_SAMOAN | ?wxLANGUAGE_SANGHO | ?wxLANGUAGE_SANSKRIT | ?wxLANGUAGE_SCOTS_GAELIC | ?wxLANGUAGE_SERBIAN | ?wxLANGUAGE_SERBIAN_CYRILLIC | ?wxLANGUAGE_SERBIAN_LATIN | ?wxLANGUAGE_SERBO_CROATIAN | ?wxLANGUAGE_SESOTHO | ?wxLANGUAGE_SETSWANA | ?wxLANGUAGE_SHONA | ?wxLANGUAGE_SINDHI | ?wxLANGUAGE_SINHALESE | ?wxLANGUAGE_SISWATI | ?wxLANGUAGE_SLOVAK | ?wxLANGUAGE_SLOVENIAN | ?wxLANGUAGE_SOMALI | ?wxLANGUAGE_SPANISH | ?wxLANGUAGE_SPANISH_ARGENTINA | ?wxLANGUAGE_SPANISH_BOLIVIA | ?wxLANGUAGE_SPANISH_CHILE | ?wxLANGUAGE_SPANISH_COLOMBIA | ?wxLANGUAGE_SPANISH_COSTA_RICA | ?wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC | ?wxLANGUAGE_SPANISH_ECUADOR | ?wxLANGUAGE_SPANISH_EL_SALVADOR | ?wxLANGUAGE_SPANISH_GUATEMALA | ?wxLANGUAGE_SPANISH_HONDURAS | ?wxLANGUAGE_SPANISH_MEXICAN | ?wxLANGUAGE_SPANISH_MODERN | ?wxLANGUAGE_SPANISH_NICARAGUA | ?wxLANGUAGE_SPANISH_PANAMA | ?wxLANGUAGE_SPANISH_PARAGUAY | ?wxLANGUAGE_SPANISH_PERU | ?wxLANGUAGE_SPANISH_PUERTO_RICO | ?wxLANGUAGE_SPANISH_URUGUAY | ?wxLANGUAGE_SPANISH_US | ?wxLANGUAGE_SPANISH_VENEZUELA | ?wxLANGUAGE_SUNDANESE | ?wxLANGUAGE_SWAHILI | ?wxLANGUAGE_SWEDISH | ?wxLANGUAGE_SWEDISH_FINLAND | ?wxLANGUAGE_TAGALOG | ?wxLANGUAGE_TAJIK | ?wxLANGUAGE_TAMIL | ?wxLANGUAGE_TATAR | ?wxLANGUAGE_TELUGU | ?wxLANGUAGE_THAI | ?wxLANGUAGE_TIBETAN | ?wxLANGUAGE_TIGRINYA | ?wxLANGUAGE_TONGA | ?wxLANGUAGE_TSONGA | ?wxLANGUAGE_TURKISH | ?wxLANGUAGE_TURKMEN | ?wxLANGUAGE_TWI | ?wxLANGUAGE_UIGHUR | ?wxLANGUAGE_UKRAINIAN | ?wxLANGUAGE_URDU | ?wxLANGUAGE_URDU_INDIA | ?wxLANGUAGE_URDU_PAKISTAN | ?wxLANGUAGE_UZBEK | ?wxLANGUAGE_UZBEK_CYRILLIC | ?wxLANGUAGE_UZBEK_LATIN | ?wxLANGUAGE_VIETNAMESE | ?wxLANGUAGE_VOLAPUK | ?wxLANGUAGE_WELSH | ?wxLANGUAGE_WOLOF | ?wxLANGUAGE_XHOSA | ?wxLANGUAGE_YIDDISH | ?wxLANGUAGE_YORUBA | ?wxLANGUAGE_ZHUANG | ?wxLANGUAGE_ZULU | ?wxLANGUAGE_USER_DEFINED | ?wxLANGUAGE_VALENCIAN | ?wxLANGUAGE_SAMI
+-spec addCatalog(This, SzDomain, MsgIdLanguage, MsgIdCharset) -> boolean() when
+ This::wxLocale(), SzDomain::unicode:chardata(), MsgIdLanguage::wx:wx_enum(), MsgIdCharset::unicode:chardata().
+addCatalog(#wx_ref{type=ThisT,ref=ThisRef},SzDomain,MsgIdLanguage,MsgIdCharset)
+ when is_list(SzDomain),is_integer(MsgIdLanguage),is_list(MsgIdCharset) ->
+ ?CLASS(ThisT,wxLocale),
+ SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),
+ MsgIdCharset_UC = unicode:characters_to_binary([MsgIdCharset,0]),
+ wxe_util:call(?wxLocale_AddCatalog_3,
+ <<ThisRef:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8,MsgIdLanguage:32/?UI,(byte_size(MsgIdCharset_UC)):32/?UI,(MsgIdCharset_UC)/binary, 0:(((8- ((0+byte_size(MsgIdCharset_UC)) band 16#7)) band 16#7))/unit:8>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleaddcataloglookuppathprefix">external documentation</a>.
+-spec addCatalogLookupPathPrefix(Prefix) -> ok when
+ Prefix::unicode:chardata().
+addCatalogLookupPathPrefix(Prefix)
+ when is_list(Prefix) ->
+ Prefix_UC = unicode:characters_to_binary([Prefix,0]),
+ wxe_util:cast(?wxLocale_AddCatalogLookupPathPrefix,
+ <<(byte_size(Prefix_UC)):32/?UI,(Prefix_UC)/binary, 0:(((8- ((4+byte_size(Prefix_UC)) band 16#7)) band 16#7))/unit:8>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetcanonicalname">external documentation</a>.
+-spec getCanonicalName(This) -> unicode:charlist() when
+ This::wxLocale().
+getCanonicalName(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_GetCanonicalName,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetlanguage">external documentation</a>.
+-spec getLanguage(This) -> integer() when
+ This::wxLocale().
+getLanguage(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_GetLanguage,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetlanguagename">external documentation</a>.
+-spec getLanguageName(Lang) -> unicode:charlist() when
+ Lang::integer().
+getLanguageName(Lang)
+ when is_integer(Lang) ->
+ wxe_util:call(?wxLocale_GetLanguageName,
+ <<Lang:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetlocale">external documentation</a>.
+-spec getLocale(This) -> unicode:charlist() when
+ This::wxLocale().
+getLocale(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_GetLocale,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetname">external documentation</a>.
+-spec getName(This) -> unicode:charlist() when
+ This::wxLocale().
+getName(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_GetName,
+ <<ThisRef:32/?UI>>).
+
+%% @equiv getString(This,SzOrigString, [])
+-spec getString(This, SzOrigString) -> unicode:charlist() when
+ This::wxLocale(), SzOrigString::unicode:chardata().
+
+getString(This,SzOrigString)
+ when is_record(This, wx_ref),is_list(SzOrigString) ->
+ getString(This,SzOrigString, []).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetstring">external documentation</a>.
+-spec getString(This, SzOrigString, [Option]) -> unicode:charlist() when
+ This::wxLocale(), SzOrigString::unicode:chardata(),
+ Option :: {szDomain, unicode:chardata()}.
+getString(#wx_ref{type=ThisT,ref=ThisRef},SzOrigString, Options)
+ when is_list(SzOrigString),is_list(Options) ->
+ ?CLASS(ThisT,wxLocale),
+ SzOrigString_UC = unicode:characters_to_binary([SzOrigString,0]),
+ MOpts = fun({szDomain, SzDomain}, Acc) -> SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),[<<1:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:call(?wxLocale_GetString_2,
+ <<ThisRef:32/?UI,(byte_size(SzOrigString_UC)):32/?UI,(SzOrigString_UC)/binary, 0:(((8- ((0+byte_size(SzOrigString_UC)) band 16#7)) band 16#7))/unit:8, BinOpt/binary>>).
+
+%% @equiv getString(This,SzOrigString,SzOrigString2,N, [])
+-spec getString(This, SzOrigString, SzOrigString2, N) -> unicode:charlist() when
+ This::wxLocale(), SzOrigString::unicode:chardata(), SzOrigString2::unicode:chardata(), N::integer().
+
+getString(This,SzOrigString,SzOrigString2,N)
+ when is_record(This, wx_ref),is_list(SzOrigString),is_list(SzOrigString2),is_integer(N) ->
+ getString(This,SzOrigString,SzOrigString2,N, []).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetstring">external documentation</a>.
+-spec getString(This, SzOrigString, SzOrigString2, N, [Option]) -> unicode:charlist() when
+ This::wxLocale(), SzOrigString::unicode:chardata(), SzOrigString2::unicode:chardata(), N::integer(),
+ Option :: {szDomain, unicode:chardata()}.
+getString(#wx_ref{type=ThisT,ref=ThisRef},SzOrigString,SzOrigString2,N, Options)
+ when is_list(SzOrigString),is_list(SzOrigString2),is_integer(N),is_list(Options) ->
+ ?CLASS(ThisT,wxLocale),
+ SzOrigString_UC = unicode:characters_to_binary([SzOrigString,0]),
+ SzOrigString2_UC = unicode:characters_to_binary([SzOrigString2,0]),
+ MOpts = fun({szDomain, SzDomain}, Acc) -> SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),[<<1:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:call(?wxLocale_GetString_4,
+ <<ThisRef:32/?UI,(byte_size(SzOrigString_UC)):32/?UI,(SzOrigString_UC)/binary, 0:(((8- ((0+byte_size(SzOrigString_UC)) band 16#7)) band 16#7))/unit:8,(byte_size(SzOrigString2_UC)):32/?UI,(SzOrigString2_UC)/binary, 0:(((8- ((4+byte_size(SzOrigString2_UC)) band 16#7)) band 16#7))/unit:8,N:32/?UI, 0:32,BinOpt/binary>>).
+
+%% @equiv getHeaderValue(This,SzHeader, [])
+-spec getHeaderValue(This, SzHeader) -> unicode:charlist() when
+ This::wxLocale(), SzHeader::unicode:chardata().
+
+getHeaderValue(This,SzHeader)
+ when is_record(This, wx_ref),is_list(SzHeader) ->
+ getHeaderValue(This,SzHeader, []).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetheadervalue">external documentation</a>.
+-spec getHeaderValue(This, SzHeader, [Option]) -> unicode:charlist() when
+ This::wxLocale(), SzHeader::unicode:chardata(),
+ Option :: {szDomain, unicode:chardata()}.
+getHeaderValue(#wx_ref{type=ThisT,ref=ThisRef},SzHeader, Options)
+ when is_list(SzHeader),is_list(Options) ->
+ ?CLASS(ThisT,wxLocale),
+ SzHeader_UC = unicode:characters_to_binary([SzHeader,0]),
+ MOpts = fun({szDomain, SzDomain}, Acc) -> SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),[<<1:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:call(?wxLocale_GetHeaderValue,
+ <<ThisRef:32/?UI,(byte_size(SzHeader_UC)):32/?UI,(SzHeader_UC)/binary, 0:(((8- ((0+byte_size(SzHeader_UC)) band 16#7)) band 16#7))/unit:8, BinOpt/binary>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetsysname">external documentation</a>.
+-spec getSysName(This) -> unicode:charlist() when
+ This::wxLocale().
+getSysName(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_GetSysName,
+ <<ThisRef:32/?UI>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetsystemencoding">external documentation</a>.
+%%<br /> Res = ?wxFONTENCODING_SYSTEM | ?wxFONTENCODING_DEFAULT | ?wxFONTENCODING_ISO8859_1 | ?wxFONTENCODING_ISO8859_2 | ?wxFONTENCODING_ISO8859_3 | ?wxFONTENCODING_ISO8859_4 | ?wxFONTENCODING_ISO8859_5 | ?wxFONTENCODING_ISO8859_6 | ?wxFONTENCODING_ISO8859_7 | ?wxFONTENCODING_ISO8859_8 | ?wxFONTENCODING_ISO8859_9 | ?wxFONTENCODING_ISO8859_10 | ?wxFONTENCODING_ISO8859_11 | ?wxFONTENCODING_ISO8859_12 | ?wxFONTENCODING_ISO8859_13 | ?wxFONTENCODING_ISO8859_14 | ?wxFONTENCODING_ISO8859_15 | ?wxFONTENCODING_ISO8859_MAX | ?wxFONTENCODING_KOI8 | ?wxFONTENCODING_KOI8_U | ?wxFONTENCODING_ALTERNATIVE | ?wxFONTENCODING_BULGARIAN | ?wxFONTENCODING_CP437 | ?wxFONTENCODING_CP850 | ?wxFONTENCODING_CP852 | ?wxFONTENCODING_CP855 | ?wxFONTENCODING_CP866 | ?wxFONTENCODING_CP874 | ?wxFONTENCODING_CP932 | ?wxFONTENCODING_CP936 | ?wxFONTENCODING_CP949 | ?wxFONTENCODING_CP950 | ?wxFONTENCODING_CP1250 | ?wxFONTENCODING_CP1251 | ?wxFONTENCODING_CP1252 | ?wxFONTENCODING_CP1253 | ?wxFONTENCODING_CP1254 | ?wxFONTENCODING_CP1255 | ?wxFONTENCODING_CP1256 | ?wxFONTENCODING_CP1257 | ?wxFONTENCODING_CP12_MAX | ?wxFONTENCODING_UTF7 | ?wxFONTENCODING_UTF8 | ?wxFONTENCODING_EUC_JP | ?wxFONTENCODING_UTF16BE | ?wxFONTENCODING_UTF16LE | ?wxFONTENCODING_UTF32BE | ?wxFONTENCODING_UTF32LE | ?wxFONTENCODING_MACROMAN | ?wxFONTENCODING_MACJAPANESE | ?wxFONTENCODING_MACCHINESETRAD | ?wxFONTENCODING_MACKOREAN | ?wxFONTENCODING_MACARABIC | ?wxFONTENCODING_MACHEBREW | ?wxFONTENCODING_MACGREEK | ?wxFONTENCODING_MACCYRILLIC | ?wxFONTENCODING_MACDEVANAGARI | ?wxFONTENCODING_MACGURMUKHI | ?wxFONTENCODING_MACGUJARATI | ?wxFONTENCODING_MACORIYA | ?wxFONTENCODING_MACBENGALI | ?wxFONTENCODING_MACTAMIL | ?wxFONTENCODING_MACTELUGU | ?wxFONTENCODING_MACKANNADA | ?wxFONTENCODING_MACMALAJALAM | ?wxFONTENCODING_MACSINHALESE | ?wxFONTENCODING_MACBURMESE | ?wxFONTENCODING_MACKHMER | ?wxFONTENCODING_MACTHAI | ?wxFONTENCODING_MACLAOTIAN | ?wxFONTENCODING_MACGEORGIAN | ?wxFONTENCODING_MACARMENIAN | ?wxFONTENCODING_MACCHINESESIMP | ?wxFONTENCODING_MACTIBETAN | ?wxFONTENCODING_MACMONGOLIAN | ?wxFONTENCODING_MACETHIOPIC | ?wxFONTENCODING_MACCENTRALEUR | ?wxFONTENCODING_MACVIATNAMESE | ?wxFONTENCODING_MACARABICEXT | ?wxFONTENCODING_MACSYMBOL | ?wxFONTENCODING_MACDINGBATS | ?wxFONTENCODING_MACTURKISH | ?wxFONTENCODING_MACCROATIAN | ?wxFONTENCODING_MACICELANDIC | ?wxFONTENCODING_MACROMANIAN | ?wxFONTENCODING_MACCELTIC | ?wxFONTENCODING_MACGAELIC | ?wxFONTENCODING_MACKEYBOARD | ?wxFONTENCODING_MAX | ?wxFONTENCODING_MACMIN | ?wxFONTENCODING_MACMAX | ?wxFONTENCODING_UTF16 | ?wxFONTENCODING_UTF32 | ?wxFONTENCODING_UNICODE | ?wxFONTENCODING_GB2312 | ?wxFONTENCODING_BIG5 | ?wxFONTENCODING_SHIFT_JIS
+-spec getSystemEncoding() -> wx:wx_enum().
+getSystemEncoding() ->
+ wxe_util:call(?wxLocale_GetSystemEncoding,
+ <<>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetsystemencodingname">external documentation</a>.
+-spec getSystemEncodingName() -> unicode:charlist().
+getSystemEncodingName() ->
+ wxe_util:call(?wxLocale_GetSystemEncodingName,
+ <<>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocalegetsystemlanguage">external documentation</a>.
+-spec getSystemLanguage() -> integer().
+getSystemLanguage() ->
+ wxe_util:call(?wxLocale_GetSystemLanguage,
+ <<>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleisloaded">external documentation</a>.
+-spec isLoaded(This, SzDomain) -> boolean() when
+ This::wxLocale(), SzDomain::unicode:chardata().
+isLoaded(#wx_ref{type=ThisT,ref=ThisRef},SzDomain)
+ when is_list(SzDomain) ->
+ ?CLASS(ThisT,wxLocale),
+ SzDomain_UC = unicode:characters_to_binary([SzDomain,0]),
+ wxe_util:call(?wxLocale_IsLoaded,
+ <<ThisRef:32/?UI,(byte_size(SzDomain_UC)):32/?UI,(SzDomain_UC)/binary, 0:(((8- ((0+byte_size(SzDomain_UC)) band 16#7)) band 16#7))/unit:8>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlocale.html#wxlocaleisok">external documentation</a>.
+-spec isOk(This) -> boolean() when
+ This::wxLocale().
+isOk(#wx_ref{type=ThisT,ref=ThisRef}) ->
+ ?CLASS(ThisT,wxLocale),
+ wxe_util:call(?wxLocale_IsOk,
+ <<ThisRef:32/?UI>>).
+
+%% @doc Destroys this object, do not use object again
+-spec destroy(This::wxLocale()) -> ok.
+destroy(Obj=#wx_ref{type=Type}) ->
+ ?CLASS(Type,wxLocale),
+ wxe_util:destroy(?wxLocale_destruct,Obj),
+ ok.
diff --git a/lib/wx/src/gen/wx_misc.erl b/lib/wx/src/gen/wx_misc.erl
index 96912ce651..397d48b727 100644
--- a/lib/wx/src/gen/wx_misc.erl
+++ b/lib/wx/src/gen/wx_misc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,6 +30,8 @@
launchDefaultBrowser/2,newId/0,registerId/1,setDetectableAutoRepeat/1,
shell/0,shell/1,shutdown/1]).
+-export([displaySize/0,setCursor/1]).
+
%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_miscellany.html#wxgetkeystate">external documentation</a>.
%%<br /> Key = ?WXK_BACK | ?WXK_TAB | ?WXK_RETURN | ?WXK_ESCAPE | ?WXK_SPACE | ?WXK_DELETE | ?WXK_START | ?WXK_LBUTTON | ?WXK_RBUTTON | ?WXK_CANCEL | ?WXK_MBUTTON | ?WXK_CLEAR | ?WXK_SHIFT | ?WXK_ALT | ?WXK_CONTROL | ?WXK_MENU | ?WXK_PAUSE | ?WXK_CAPITAL | ?WXK_END | ?WXK_HOME | ?WXK_LEFT | ?WXK_UP | ?WXK_RIGHT | ?WXK_DOWN | ?WXK_SELECT | ?WXK_PRINT | ?WXK_EXECUTE | ?WXK_SNAPSHOT | ?WXK_INSERT | ?WXK_HELP | ?WXK_NUMPAD0 | ?WXK_NUMPAD1 | ?WXK_NUMPAD2 | ?WXK_NUMPAD3 | ?WXK_NUMPAD4 | ?WXK_NUMPAD5 | ?WXK_NUMPAD6 | ?WXK_NUMPAD7 | ?WXK_NUMPAD8 | ?WXK_NUMPAD9 | ?WXK_MULTIPLY | ?WXK_ADD | ?WXK_SEPARATOR | ?WXK_SUBTRACT | ?WXK_DECIMAL | ?WXK_DIVIDE | ?WXK_F1 | ?WXK_F2 | ?WXK_F3 | ?WXK_F4 | ?WXK_F5 | ?WXK_F6 | ?WXK_F7 | ?WXK_F8 | ?WXK_F9 | ?WXK_F10 | ?WXK_F11 | ?WXK_F12 | ?WXK_F13 | ?WXK_F14 | ?WXK_F15 | ?WXK_F16 | ?WXK_F17 | ?WXK_F18 | ?WXK_F19 | ?WXK_F20 | ?WXK_F21 | ?WXK_F22 | ?WXK_F23 | ?WXK_F24 | ?WXK_NUMLOCK | ?WXK_SCROLL | ?WXK_PAGEUP | ?WXK_PAGEDOWN | ?WXK_NUMPAD_SPACE | ?WXK_NUMPAD_TAB | ?WXK_NUMPAD_ENTER | ?WXK_NUMPAD_F1 | ?WXK_NUMPAD_F2 | ?WXK_NUMPAD_F3 | ?WXK_NUMPAD_F4 | ?WXK_NUMPAD_HOME | ?WXK_NUMPAD_LEFT | ?WXK_NUMPAD_UP | ?WXK_NUMPAD_RIGHT | ?WXK_NUMPAD_DOWN | ?WXK_NUMPAD_PAGEUP | ?WXK_NUMPAD_PAGEDOWN | ?WXK_NUMPAD_END | ?WXK_NUMPAD_BEGIN | ?WXK_NUMPAD_INSERT | ?WXK_NUMPAD_DELETE | ?WXK_NUMPAD_EQUAL | ?WXK_NUMPAD_MULTIPLY | ?WXK_NUMPAD_ADD | ?WXK_NUMPAD_SEPARATOR | ?WXK_NUMPAD_SUBTRACT | ?WXK_NUMPAD_DECIMAL | ?WXK_NUMPAD_DIVIDE | ?WXK_WINDOWS_LEFT | ?WXK_WINDOWS_RIGHT | ?WXK_WINDOWS_MENU | ?WXK_COMMAND | ?WXK_SPECIAL1 | ?WXK_SPECIAL2 | ?WXK_SPECIAL3 | ?WXK_SPECIAL4 | ?WXK_SPECIAL5 | ?WXK_SPECIAL6 | ?WXK_SPECIAL7 | ?WXK_SPECIAL8 | ?WXK_SPECIAL9 | ?WXK_SPECIAL10 | ?WXK_SPECIAL11 | ?WXK_SPECIAL12 | ?WXK_SPECIAL13 | ?WXK_SPECIAL14 | ?WXK_SPECIAL15 | ?WXK_SPECIAL16 | ?WXK_SPECIAL17 | ?WXK_SPECIAL18 | ?WXK_SPECIAL19 | ?WXK_SPECIAL20
-spec getKeyState(Key) -> boolean() when
@@ -224,3 +226,17 @@ isPlatform64Bit() ->
wxe_util:call(?utils_wxIsPlatform64Bit,
<<>>).
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_gdicmn.html#gdicmnwxdisplaysize">external documentation</a>.
+-spec displaySize() -> {Width::integer(), Height::integer()}.
+displaySize() ->
+ wxe_util:call(?gdicmn_wxDisplaySize,
+ <<>>).
+
+%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_gdicmn.html#gdicmnwxsetcursor">external documentation</a>.
+-spec setCursor(Cursor) -> ok when
+ Cursor::wxCursor:wxCursor().
+setCursor(#wx_ref{type=CursorT,ref=CursorRef}) ->
+ ?CLASS(CursorT,wxCursor),
+ wxe_util:cast(?gdicmn_wxSetCursor,
+ <<CursorRef:32/?UI>>).
+
diff --git a/lib/wx/src/gen/wxe_debug.hrl b/lib/wx/src/gen/wxe_debug.hrl
index 1f4f22f704..e208b081e8 100644
--- a/lib/wx/src/gen/wxe_debug.hrl
+++ b/lib/wx/src/gen/wxe_debug.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -2709,602 +2709,625 @@ wxdebug_table() ->
{2924, {utils, getOsDescription, 0}},
{2925, {utils, isPlatformLittleEndian, 0}},
{2926, {utils, isPlatform64Bit, 0}},
- {2927, {wxPrintout, new, 1}},
- {2928, {wxPrintout, destruct, 0}},
- {2929, {wxPrintout, getDC, 0}},
- {2930, {wxPrintout, getPageSizeMM, 2}},
- {2931, {wxPrintout, getPageSizePixels, 2}},
- {2932, {wxPrintout, getPaperRectPixels, 0}},
- {2933, {wxPrintout, getPPIPrinter, 2}},
- {2934, {wxPrintout, getPPIScreen, 2}},
- {2935, {wxPrintout, getTitle, 0}},
- {2936, {wxPrintout, isPreview, 0}},
- {2937, {wxPrintout, fitThisSizeToPaper, 1}},
- {2938, {wxPrintout, fitThisSizeToPage, 1}},
- {2939, {wxPrintout, fitThisSizeToPageMargins, 2}},
- {2940, {wxPrintout, mapScreenSizeToPaper, 0}},
- {2941, {wxPrintout, mapScreenSizeToPage, 0}},
- {2942, {wxPrintout, mapScreenSizeToPageMargins, 1}},
- {2943, {wxPrintout, mapScreenSizeToDevice, 0}},
- {2944, {wxPrintout, getLogicalPaperRect, 0}},
- {2945, {wxPrintout, getLogicalPageRect, 0}},
- {2946, {wxPrintout, getLogicalPageMarginsRect, 1}},
- {2947, {wxPrintout, setLogicalOrigin, 2}},
- {2948, {wxPrintout, offsetLogicalOrigin, 2}},
- {2949, {wxStyledTextCtrl, new_2, 2}},
- {2950, {wxStyledTextCtrl, new_0, 0}},
- {2951, {wxStyledTextCtrl, destruct, 0}},
- {2952, {wxStyledTextCtrl, create, 2}},
- {2953, {wxStyledTextCtrl, addText, 1}},
- {2954, {wxStyledTextCtrl, addStyledText, 1}},
- {2955, {wxStyledTextCtrl, insertText, 2}},
- {2956, {wxStyledTextCtrl, clearAll, 0}},
- {2957, {wxStyledTextCtrl, clearDocumentStyle, 0}},
- {2958, {wxStyledTextCtrl, getLength, 0}},
- {2959, {wxStyledTextCtrl, getCharAt, 1}},
- {2960, {wxStyledTextCtrl, getCurrentPos, 0}},
- {2961, {wxStyledTextCtrl, getAnchor, 0}},
- {2962, {wxStyledTextCtrl, getStyleAt, 1}},
- {2963, {wxStyledTextCtrl, redo, 0}},
- {2964, {wxStyledTextCtrl, setUndoCollection, 1}},
- {2965, {wxStyledTextCtrl, selectAll, 0}},
- {2966, {wxStyledTextCtrl, setSavePoint, 0}},
- {2967, {wxStyledTextCtrl, getStyledText, 2}},
- {2968, {wxStyledTextCtrl, canRedo, 0}},
- {2969, {wxStyledTextCtrl, markerLineFromHandle, 1}},
- {2970, {wxStyledTextCtrl, markerDeleteHandle, 1}},
- {2971, {wxStyledTextCtrl, getUndoCollection, 0}},
- {2972, {wxStyledTextCtrl, getViewWhiteSpace, 0}},
- {2973, {wxStyledTextCtrl, setViewWhiteSpace, 1}},
- {2974, {wxStyledTextCtrl, positionFromPoint, 1}},
- {2975, {wxStyledTextCtrl, positionFromPointClose, 2}},
- {2976, {wxStyledTextCtrl, gotoLine, 1}},
- {2977, {wxStyledTextCtrl, gotoPos, 1}},
- {2978, {wxStyledTextCtrl, setAnchor, 1}},
- {2979, {wxStyledTextCtrl, getCurLine, 1}},
- {2980, {wxStyledTextCtrl, getEndStyled, 0}},
- {2981, {wxStyledTextCtrl, convertEOLs, 1}},
- {2982, {wxStyledTextCtrl, getEOLMode, 0}},
- {2983, {wxStyledTextCtrl, setEOLMode, 1}},
- {2984, {wxStyledTextCtrl, startStyling, 2}},
- {2985, {wxStyledTextCtrl, setStyling, 2}},
- {2986, {wxStyledTextCtrl, getBufferedDraw, 0}},
- {2987, {wxStyledTextCtrl, setBufferedDraw, 1}},
- {2988, {wxStyledTextCtrl, setTabWidth, 1}},
- {2989, {wxStyledTextCtrl, getTabWidth, 0}},
- {2990, {wxStyledTextCtrl, setCodePage, 1}},
- {2991, {wxStyledTextCtrl, markerDefine, 3}},
- {2992, {wxStyledTextCtrl, markerSetForeground, 2}},
- {2993, {wxStyledTextCtrl, markerSetBackground, 2}},
- {2994, {wxStyledTextCtrl, markerAdd, 2}},
- {2995, {wxStyledTextCtrl, markerDelete, 2}},
- {2996, {wxStyledTextCtrl, markerDeleteAll, 1}},
- {2997, {wxStyledTextCtrl, markerGet, 1}},
- {2998, {wxStyledTextCtrl, markerNext, 2}},
- {2999, {wxStyledTextCtrl, markerPrevious, 2}},
- {3000, {wxStyledTextCtrl, markerDefineBitmap, 2}},
- {3001, {wxStyledTextCtrl, markerAddSet, 2}},
- {3002, {wxStyledTextCtrl, markerSetAlpha, 2}},
- {3003, {wxStyledTextCtrl, setMarginType, 2}},
- {3004, {wxStyledTextCtrl, getMarginType, 1}},
- {3005, {wxStyledTextCtrl, setMarginWidth, 2}},
- {3006, {wxStyledTextCtrl, getMarginWidth, 1}},
- {3007, {wxStyledTextCtrl, setMarginMask, 2}},
- {3008, {wxStyledTextCtrl, getMarginMask, 1}},
- {3009, {wxStyledTextCtrl, setMarginSensitive, 2}},
- {3010, {wxStyledTextCtrl, getMarginSensitive, 1}},
- {3011, {wxStyledTextCtrl, styleClearAll, 0}},
- {3012, {wxStyledTextCtrl, styleSetForeground, 2}},
- {3013, {wxStyledTextCtrl, styleSetBackground, 2}},
- {3014, {wxStyledTextCtrl, styleSetBold, 2}},
- {3015, {wxStyledTextCtrl, styleSetItalic, 2}},
- {3016, {wxStyledTextCtrl, styleSetSize, 2}},
- {3017, {wxStyledTextCtrl, styleSetFaceName, 2}},
- {3018, {wxStyledTextCtrl, styleSetEOLFilled, 2}},
- {3019, {wxStyledTextCtrl, styleResetDefault, 0}},
- {3020, {wxStyledTextCtrl, styleSetUnderline, 2}},
- {3021, {wxStyledTextCtrl, styleSetCase, 2}},
- {3022, {wxStyledTextCtrl, styleSetHotSpot, 2}},
- {3023, {wxStyledTextCtrl, setSelForeground, 2}},
- {3024, {wxStyledTextCtrl, setSelBackground, 2}},
- {3025, {wxStyledTextCtrl, getSelAlpha, 0}},
- {3026, {wxStyledTextCtrl, setSelAlpha, 1}},
- {3027, {wxStyledTextCtrl, setCaretForeground, 1}},
- {3028, {wxStyledTextCtrl, cmdKeyAssign, 3}},
- {3029, {wxStyledTextCtrl, cmdKeyClear, 2}},
- {3030, {wxStyledTextCtrl, cmdKeyClearAll, 0}},
- {3031, {wxStyledTextCtrl, setStyleBytes, 2}},
- {3032, {wxStyledTextCtrl, styleSetVisible, 2}},
- {3033, {wxStyledTextCtrl, getCaretPeriod, 0}},
- {3034, {wxStyledTextCtrl, setCaretPeriod, 1}},
- {3035, {wxStyledTextCtrl, setWordChars, 1}},
- {3036, {wxStyledTextCtrl, beginUndoAction, 0}},
- {3037, {wxStyledTextCtrl, endUndoAction, 0}},
- {3038, {wxStyledTextCtrl, indicatorSetStyle, 2}},
- {3039, {wxStyledTextCtrl, indicatorGetStyle, 1}},
- {3040, {wxStyledTextCtrl, indicatorSetForeground, 2}},
- {3041, {wxStyledTextCtrl, indicatorGetForeground, 1}},
- {3042, {wxStyledTextCtrl, setWhitespaceForeground, 2}},
- {3043, {wxStyledTextCtrl, setWhitespaceBackground, 2}},
- {3044, {wxStyledTextCtrl, getStyleBits, 0}},
- {3045, {wxStyledTextCtrl, setLineState, 2}},
- {3046, {wxStyledTextCtrl, getLineState, 1}},
- {3047, {wxStyledTextCtrl, getMaxLineState, 0}},
- {3048, {wxStyledTextCtrl, getCaretLineVisible, 0}},
- {3049, {wxStyledTextCtrl, setCaretLineVisible, 1}},
- {3050, {wxStyledTextCtrl, getCaretLineBackground, 0}},
- {3051, {wxStyledTextCtrl, setCaretLineBackground, 1}},
- {3052, {wxStyledTextCtrl, autoCompShow, 2}},
- {3053, {wxStyledTextCtrl, autoCompCancel, 0}},
- {3054, {wxStyledTextCtrl, autoCompActive, 0}},
- {3055, {wxStyledTextCtrl, autoCompPosStart, 0}},
- {3056, {wxStyledTextCtrl, autoCompComplete, 0}},
- {3057, {wxStyledTextCtrl, autoCompStops, 1}},
- {3058, {wxStyledTextCtrl, autoCompSetSeparator, 1}},
- {3059, {wxStyledTextCtrl, autoCompGetSeparator, 0}},
- {3060, {wxStyledTextCtrl, autoCompSelect, 1}},
- {3061, {wxStyledTextCtrl, autoCompSetCancelAtStart, 1}},
- {3062, {wxStyledTextCtrl, autoCompGetCancelAtStart, 0}},
- {3063, {wxStyledTextCtrl, autoCompSetFillUps, 1}},
- {3064, {wxStyledTextCtrl, autoCompSetChooseSingle, 1}},
- {3065, {wxStyledTextCtrl, autoCompGetChooseSingle, 0}},
- {3066, {wxStyledTextCtrl, autoCompSetIgnoreCase, 1}},
- {3067, {wxStyledTextCtrl, autoCompGetIgnoreCase, 0}},
- {3068, {wxStyledTextCtrl, userListShow, 2}},
- {3069, {wxStyledTextCtrl, autoCompSetAutoHide, 1}},
- {3070, {wxStyledTextCtrl, autoCompGetAutoHide, 0}},
- {3071, {wxStyledTextCtrl, autoCompSetDropRestOfWord, 1}},
- {3072, {wxStyledTextCtrl, autoCompGetDropRestOfWord, 0}},
- {3073, {wxStyledTextCtrl, registerImage, 2}},
- {3074, {wxStyledTextCtrl, clearRegisteredImages, 0}},
- {3075, {wxStyledTextCtrl, autoCompGetTypeSeparator, 0}},
- {3076, {wxStyledTextCtrl, autoCompSetTypeSeparator, 1}},
- {3077, {wxStyledTextCtrl, autoCompSetMaxWidth, 1}},
- {3078, {wxStyledTextCtrl, autoCompGetMaxWidth, 0}},
- {3079, {wxStyledTextCtrl, autoCompSetMaxHeight, 1}},
- {3080, {wxStyledTextCtrl, autoCompGetMaxHeight, 0}},
- {3081, {wxStyledTextCtrl, setIndent, 1}},
- {3082, {wxStyledTextCtrl, getIndent, 0}},
- {3083, {wxStyledTextCtrl, setUseTabs, 1}},
- {3084, {wxStyledTextCtrl, getUseTabs, 0}},
- {3085, {wxStyledTextCtrl, setLineIndentation, 2}},
- {3086, {wxStyledTextCtrl, getLineIndentation, 1}},
- {3087, {wxStyledTextCtrl, getLineIndentPosition, 1}},
- {3088, {wxStyledTextCtrl, getColumn, 1}},
- {3089, {wxStyledTextCtrl, setUseHorizontalScrollBar, 1}},
- {3090, {wxStyledTextCtrl, getUseHorizontalScrollBar, 0}},
- {3091, {wxStyledTextCtrl, setIndentationGuides, 1}},
- {3092, {wxStyledTextCtrl, getIndentationGuides, 0}},
- {3093, {wxStyledTextCtrl, setHighlightGuide, 1}},
- {3094, {wxStyledTextCtrl, getHighlightGuide, 0}},
- {3095, {wxStyledTextCtrl, getLineEndPosition, 1}},
- {3096, {wxStyledTextCtrl, getCodePage, 0}},
- {3097, {wxStyledTextCtrl, getCaretForeground, 0}},
- {3098, {wxStyledTextCtrl, getReadOnly, 0}},
- {3099, {wxStyledTextCtrl, setCurrentPos, 1}},
- {3100, {wxStyledTextCtrl, setSelectionStart, 1}},
- {3101, {wxStyledTextCtrl, getSelectionStart, 0}},
- {3102, {wxStyledTextCtrl, setSelectionEnd, 1}},
- {3103, {wxStyledTextCtrl, getSelectionEnd, 0}},
- {3104, {wxStyledTextCtrl, setPrintMagnification, 1}},
- {3105, {wxStyledTextCtrl, getPrintMagnification, 0}},
- {3106, {wxStyledTextCtrl, setPrintColourMode, 1}},
- {3107, {wxStyledTextCtrl, getPrintColourMode, 0}},
- {3108, {wxStyledTextCtrl, findText, 4}},
- {3109, {wxStyledTextCtrl, formatRange, 7}},
- {3110, {wxStyledTextCtrl, getFirstVisibleLine, 0}},
- {3111, {wxStyledTextCtrl, getLine, 1}},
- {3112, {wxStyledTextCtrl, getLineCount, 0}},
- {3113, {wxStyledTextCtrl, setMarginLeft, 1}},
- {3114, {wxStyledTextCtrl, getMarginLeft, 0}},
- {3115, {wxStyledTextCtrl, setMarginRight, 1}},
- {3116, {wxStyledTextCtrl, getMarginRight, 0}},
- {3117, {wxStyledTextCtrl, getModify, 0}},
- {3118, {wxStyledTextCtrl, setSelection, 2}},
- {3119, {wxStyledTextCtrl, getSelectedText, 0}},
- {3120, {wxStyledTextCtrl, getTextRange, 2}},
- {3121, {wxStyledTextCtrl, hideSelection, 1}},
- {3122, {wxStyledTextCtrl, lineFromPosition, 1}},
- {3123, {wxStyledTextCtrl, positionFromLine, 1}},
- {3124, {wxStyledTextCtrl, lineScroll, 2}},
- {3125, {wxStyledTextCtrl, ensureCaretVisible, 0}},
- {3126, {wxStyledTextCtrl, replaceSelection, 1}},
- {3127, {wxStyledTextCtrl, setReadOnly, 1}},
- {3128, {wxStyledTextCtrl, canPaste, 0}},
- {3129, {wxStyledTextCtrl, canUndo, 0}},
- {3130, {wxStyledTextCtrl, emptyUndoBuffer, 0}},
- {3131, {wxStyledTextCtrl, undo, 0}},
- {3132, {wxStyledTextCtrl, cut, 0}},
- {3133, {wxStyledTextCtrl, copy, 0}},
- {3134, {wxStyledTextCtrl, paste, 0}},
- {3135, {wxStyledTextCtrl, clear, 0}},
- {3136, {wxStyledTextCtrl, setText, 1}},
- {3137, {wxStyledTextCtrl, getText, 0}},
- {3138, {wxStyledTextCtrl, getTextLength, 0}},
- {3139, {wxStyledTextCtrl, getOvertype, 0}},
- {3140, {wxStyledTextCtrl, setCaretWidth, 1}},
- {3141, {wxStyledTextCtrl, getCaretWidth, 0}},
- {3142, {wxStyledTextCtrl, setTargetStart, 1}},
- {3143, {wxStyledTextCtrl, getTargetStart, 0}},
- {3144, {wxStyledTextCtrl, setTargetEnd, 1}},
- {3145, {wxStyledTextCtrl, getTargetEnd, 0}},
- {3146, {wxStyledTextCtrl, replaceTarget, 1}},
- {3147, {wxStyledTextCtrl, searchInTarget, 1}},
- {3148, {wxStyledTextCtrl, setSearchFlags, 1}},
- {3149, {wxStyledTextCtrl, getSearchFlags, 0}},
- {3150, {wxStyledTextCtrl, callTipShow, 2}},
- {3151, {wxStyledTextCtrl, callTipCancel, 0}},
- {3152, {wxStyledTextCtrl, callTipActive, 0}},
- {3153, {wxStyledTextCtrl, callTipPosAtStart, 0}},
- {3154, {wxStyledTextCtrl, callTipSetHighlight, 2}},
- {3155, {wxStyledTextCtrl, callTipSetBackground, 1}},
- {3156, {wxStyledTextCtrl, callTipSetForeground, 1}},
- {3157, {wxStyledTextCtrl, callTipSetForegroundHighlight, 1}},
- {3158, {wxStyledTextCtrl, callTipUseStyle, 1}},
- {3159, {wxStyledTextCtrl, visibleFromDocLine, 1}},
- {3160, {wxStyledTextCtrl, docLineFromVisible, 1}},
- {3161, {wxStyledTextCtrl, wrapCount, 1}},
- {3162, {wxStyledTextCtrl, setFoldLevel, 2}},
- {3163, {wxStyledTextCtrl, getFoldLevel, 1}},
- {3164, {wxStyledTextCtrl, getLastChild, 2}},
- {3165, {wxStyledTextCtrl, getFoldParent, 1}},
- {3166, {wxStyledTextCtrl, showLines, 2}},
- {3167, {wxStyledTextCtrl, hideLines, 2}},
- {3168, {wxStyledTextCtrl, getLineVisible, 1}},
- {3169, {wxStyledTextCtrl, setFoldExpanded, 2}},
- {3170, {wxStyledTextCtrl, getFoldExpanded, 1}},
- {3171, {wxStyledTextCtrl, toggleFold, 1}},
- {3172, {wxStyledTextCtrl, ensureVisible, 1}},
- {3173, {wxStyledTextCtrl, setFoldFlags, 1}},
- {3174, {wxStyledTextCtrl, ensureVisibleEnforcePolicy, 1}},
- {3175, {wxStyledTextCtrl, setTabIndents, 1}},
- {3176, {wxStyledTextCtrl, getTabIndents, 0}},
- {3177, {wxStyledTextCtrl, setBackSpaceUnIndents, 1}},
- {3178, {wxStyledTextCtrl, getBackSpaceUnIndents, 0}},
- {3179, {wxStyledTextCtrl, setMouseDwellTime, 1}},
- {3180, {wxStyledTextCtrl, getMouseDwellTime, 0}},
- {3181, {wxStyledTextCtrl, wordStartPosition, 2}},
- {3182, {wxStyledTextCtrl, wordEndPosition, 2}},
- {3183, {wxStyledTextCtrl, setWrapMode, 1}},
- {3184, {wxStyledTextCtrl, getWrapMode, 0}},
- {3185, {wxStyledTextCtrl, setWrapVisualFlags, 1}},
- {3186, {wxStyledTextCtrl, getWrapVisualFlags, 0}},
- {3187, {wxStyledTextCtrl, setWrapVisualFlagsLocation, 1}},
- {3188, {wxStyledTextCtrl, getWrapVisualFlagsLocation, 0}},
- {3189, {wxStyledTextCtrl, setWrapStartIndent, 1}},
- {3190, {wxStyledTextCtrl, getWrapStartIndent, 0}},
- {3191, {wxStyledTextCtrl, setLayoutCache, 1}},
- {3192, {wxStyledTextCtrl, getLayoutCache, 0}},
- {3193, {wxStyledTextCtrl, setScrollWidth, 1}},
- {3194, {wxStyledTextCtrl, getScrollWidth, 0}},
- {3195, {wxStyledTextCtrl, textWidth, 2}},
- {3196, {wxStyledTextCtrl, getEndAtLastLine, 0}},
- {3197, {wxStyledTextCtrl, textHeight, 1}},
- {3198, {wxStyledTextCtrl, setUseVerticalScrollBar, 1}},
- {3199, {wxStyledTextCtrl, getUseVerticalScrollBar, 0}},
- {3200, {wxStyledTextCtrl, appendText, 1}},
- {3201, {wxStyledTextCtrl, getTwoPhaseDraw, 0}},
- {3202, {wxStyledTextCtrl, setTwoPhaseDraw, 1}},
- {3203, {wxStyledTextCtrl, targetFromSelection, 0}},
- {3204, {wxStyledTextCtrl, linesJoin, 0}},
- {3205, {wxStyledTextCtrl, linesSplit, 1}},
- {3206, {wxStyledTextCtrl, setFoldMarginColour, 2}},
- {3207, {wxStyledTextCtrl, setFoldMarginHiColour, 2}},
- {3208, {wxStyledTextCtrl, lineDown, 0}},
- {3209, {wxStyledTextCtrl, lineDownExtend, 0}},
- {3210, {wxStyledTextCtrl, lineUp, 0}},
- {3211, {wxStyledTextCtrl, lineUpExtend, 0}},
- {3212, {wxStyledTextCtrl, charLeft, 0}},
- {3213, {wxStyledTextCtrl, charLeftExtend, 0}},
- {3214, {wxStyledTextCtrl, charRight, 0}},
- {3215, {wxStyledTextCtrl, charRightExtend, 0}},
- {3216, {wxStyledTextCtrl, wordLeft, 0}},
- {3217, {wxStyledTextCtrl, wordLeftExtend, 0}},
- {3218, {wxStyledTextCtrl, wordRight, 0}},
- {3219, {wxStyledTextCtrl, wordRightExtend, 0}},
- {3220, {wxStyledTextCtrl, home, 0}},
- {3221, {wxStyledTextCtrl, homeExtend, 0}},
- {3222, {wxStyledTextCtrl, lineEnd, 0}},
- {3223, {wxStyledTextCtrl, lineEndExtend, 0}},
- {3224, {wxStyledTextCtrl, documentStart, 0}},
- {3225, {wxStyledTextCtrl, documentStartExtend, 0}},
- {3226, {wxStyledTextCtrl, documentEnd, 0}},
- {3227, {wxStyledTextCtrl, documentEndExtend, 0}},
- {3228, {wxStyledTextCtrl, pageUp, 0}},
- {3229, {wxStyledTextCtrl, pageUpExtend, 0}},
- {3230, {wxStyledTextCtrl, pageDown, 0}},
- {3231, {wxStyledTextCtrl, pageDownExtend, 0}},
- {3232, {wxStyledTextCtrl, editToggleOvertype, 0}},
- {3233, {wxStyledTextCtrl, cancel, 0}},
- {3234, {wxStyledTextCtrl, deleteBack, 0}},
- {3235, {wxStyledTextCtrl, tab, 0}},
- {3236, {wxStyledTextCtrl, backTab, 0}},
- {3237, {wxStyledTextCtrl, newLine, 0}},
- {3238, {wxStyledTextCtrl, formFeed, 0}},
- {3239, {wxStyledTextCtrl, vCHome, 0}},
- {3240, {wxStyledTextCtrl, vCHomeExtend, 0}},
- {3241, {wxStyledTextCtrl, zoomIn, 0}},
- {3242, {wxStyledTextCtrl, zoomOut, 0}},
- {3243, {wxStyledTextCtrl, delWordLeft, 0}},
- {3244, {wxStyledTextCtrl, delWordRight, 0}},
- {3245, {wxStyledTextCtrl, lineCut, 0}},
- {3246, {wxStyledTextCtrl, lineDelete, 0}},
- {3247, {wxStyledTextCtrl, lineTranspose, 0}},
- {3248, {wxStyledTextCtrl, lineDuplicate, 0}},
- {3249, {wxStyledTextCtrl, lowerCase, 0}},
- {3250, {wxStyledTextCtrl, upperCase, 0}},
- {3251, {wxStyledTextCtrl, lineScrollDown, 0}},
- {3252, {wxStyledTextCtrl, lineScrollUp, 0}},
- {3253, {wxStyledTextCtrl, deleteBackNotLine, 0}},
- {3254, {wxStyledTextCtrl, homeDisplay, 0}},
- {3255, {wxStyledTextCtrl, homeDisplayExtend, 0}},
- {3256, {wxStyledTextCtrl, lineEndDisplay, 0}},
- {3257, {wxStyledTextCtrl, lineEndDisplayExtend, 0}},
- {3258, {wxStyledTextCtrl, homeWrapExtend, 0}},
- {3259, {wxStyledTextCtrl, lineEndWrap, 0}},
- {3260, {wxStyledTextCtrl, lineEndWrapExtend, 0}},
- {3261, {wxStyledTextCtrl, vCHomeWrap, 0}},
- {3262, {wxStyledTextCtrl, vCHomeWrapExtend, 0}},
- {3263, {wxStyledTextCtrl, lineCopy, 0}},
- {3264, {wxStyledTextCtrl, moveCaretInsideView, 0}},
- {3265, {wxStyledTextCtrl, lineLength, 1}},
- {3266, {wxStyledTextCtrl, braceHighlight, 2}},
- {3267, {wxStyledTextCtrl, braceBadLight, 1}},
- {3268, {wxStyledTextCtrl, braceMatch, 1}},
- {3269, {wxStyledTextCtrl, getViewEOL, 0}},
- {3270, {wxStyledTextCtrl, setViewEOL, 1}},
- {3271, {wxStyledTextCtrl, setModEventMask, 1}},
- {3272, {wxStyledTextCtrl, getEdgeColumn, 0}},
- {3273, {wxStyledTextCtrl, setEdgeColumn, 1}},
- {3274, {wxStyledTextCtrl, setEdgeMode, 1}},
- {3275, {wxStyledTextCtrl, getEdgeMode, 0}},
- {3276, {wxStyledTextCtrl, getEdgeColour, 0}},
- {3277, {wxStyledTextCtrl, setEdgeColour, 1}},
- {3278, {wxStyledTextCtrl, searchAnchor, 0}},
- {3279, {wxStyledTextCtrl, searchNext, 2}},
- {3280, {wxStyledTextCtrl, searchPrev, 2}},
- {3281, {wxStyledTextCtrl, linesOnScreen, 0}},
- {3282, {wxStyledTextCtrl, usePopUp, 1}},
- {3283, {wxStyledTextCtrl, selectionIsRectangle, 0}},
- {3284, {wxStyledTextCtrl, setZoom, 1}},
- {3285, {wxStyledTextCtrl, getZoom, 0}},
- {3286, {wxStyledTextCtrl, getModEventMask, 0}},
- {3287, {wxStyledTextCtrl, setSTCFocus, 1}},
- {3288, {wxStyledTextCtrl, getSTCFocus, 0}},
- {3289, {wxStyledTextCtrl, setStatus, 1}},
- {3290, {wxStyledTextCtrl, getStatus, 0}},
- {3291, {wxStyledTextCtrl, setMouseDownCaptures, 1}},
- {3292, {wxStyledTextCtrl, getMouseDownCaptures, 0}},
- {3293, {wxStyledTextCtrl, setSTCCursor, 1}},
- {3294, {wxStyledTextCtrl, getSTCCursor, 0}},
- {3295, {wxStyledTextCtrl, setControlCharSymbol, 1}},
- {3296, {wxStyledTextCtrl, getControlCharSymbol, 0}},
- {3297, {wxStyledTextCtrl, wordPartLeft, 0}},
- {3298, {wxStyledTextCtrl, wordPartLeftExtend, 0}},
- {3299, {wxStyledTextCtrl, wordPartRight, 0}},
- {3300, {wxStyledTextCtrl, wordPartRightExtend, 0}},
- {3301, {wxStyledTextCtrl, setVisiblePolicy, 2}},
- {3302, {wxStyledTextCtrl, delLineLeft, 0}},
- {3303, {wxStyledTextCtrl, delLineRight, 0}},
- {3304, {wxStyledTextCtrl, getXOffset, 0}},
- {3305, {wxStyledTextCtrl, chooseCaretX, 0}},
- {3306, {wxStyledTextCtrl, setXCaretPolicy, 2}},
- {3307, {wxStyledTextCtrl, setYCaretPolicy, 2}},
- {3308, {wxStyledTextCtrl, getPrintWrapMode, 0}},
- {3309, {wxStyledTextCtrl, setHotspotActiveForeground, 2}},
- {3310, {wxStyledTextCtrl, setHotspotActiveBackground, 2}},
- {3311, {wxStyledTextCtrl, setHotspotActiveUnderline, 1}},
- {3312, {wxStyledTextCtrl, setHotspotSingleLine, 1}},
- {3313, {wxStyledTextCtrl, paraDownExtend, 0}},
- {3314, {wxStyledTextCtrl, paraUp, 0}},
- {3315, {wxStyledTextCtrl, paraUpExtend, 0}},
- {3316, {wxStyledTextCtrl, positionBefore, 1}},
- {3317, {wxStyledTextCtrl, positionAfter, 1}},
- {3318, {wxStyledTextCtrl, copyRange, 2}},
- {3319, {wxStyledTextCtrl, copyText, 2}},
- {3320, {wxStyledTextCtrl, setSelectionMode, 1}},
- {3321, {wxStyledTextCtrl, getSelectionMode, 0}},
- {3322, {wxStyledTextCtrl, lineDownRectExtend, 0}},
- {3323, {wxStyledTextCtrl, lineUpRectExtend, 0}},
- {3324, {wxStyledTextCtrl, charLeftRectExtend, 0}},
- {3325, {wxStyledTextCtrl, charRightRectExtend, 0}},
- {3326, {wxStyledTextCtrl, homeRectExtend, 0}},
- {3327, {wxStyledTextCtrl, vCHomeRectExtend, 0}},
- {3328, {wxStyledTextCtrl, lineEndRectExtend, 0}},
- {3329, {wxStyledTextCtrl, pageUpRectExtend, 0}},
- {3330, {wxStyledTextCtrl, pageDownRectExtend, 0}},
- {3331, {wxStyledTextCtrl, stutteredPageUp, 0}},
- {3332, {wxStyledTextCtrl, stutteredPageUpExtend, 0}},
- {3333, {wxStyledTextCtrl, stutteredPageDown, 0}},
- {3334, {wxStyledTextCtrl, stutteredPageDownExtend, 0}},
- {3335, {wxStyledTextCtrl, wordLeftEnd, 0}},
- {3336, {wxStyledTextCtrl, wordLeftEndExtend, 0}},
- {3337, {wxStyledTextCtrl, wordRightEnd, 0}},
- {3338, {wxStyledTextCtrl, wordRightEndExtend, 0}},
- {3339, {wxStyledTextCtrl, setWhitespaceChars, 1}},
- {3340, {wxStyledTextCtrl, setCharsDefault, 0}},
- {3341, {wxStyledTextCtrl, autoCompGetCurrent, 0}},
- {3342, {wxStyledTextCtrl, allocate, 1}},
- {3343, {wxStyledTextCtrl, findColumn, 2}},
- {3344, {wxStyledTextCtrl, getCaretSticky, 0}},
- {3345, {wxStyledTextCtrl, setCaretSticky, 1}},
- {3346, {wxStyledTextCtrl, toggleCaretSticky, 0}},
- {3347, {wxStyledTextCtrl, setPasteConvertEndings, 1}},
- {3348, {wxStyledTextCtrl, getPasteConvertEndings, 0}},
- {3349, {wxStyledTextCtrl, selectionDuplicate, 0}},
- {3350, {wxStyledTextCtrl, setCaretLineBackAlpha, 1}},
- {3351, {wxStyledTextCtrl, getCaretLineBackAlpha, 0}},
- {3352, {wxStyledTextCtrl, startRecord, 0}},
- {3353, {wxStyledTextCtrl, stopRecord, 0}},
- {3354, {wxStyledTextCtrl, setLexer, 1}},
- {3355, {wxStyledTextCtrl, getLexer, 0}},
- {3356, {wxStyledTextCtrl, colourise, 2}},
- {3357, {wxStyledTextCtrl, setProperty, 2}},
- {3358, {wxStyledTextCtrl, setKeyWords, 2}},
- {3359, {wxStyledTextCtrl, setLexerLanguage, 1}},
- {3360, {wxStyledTextCtrl, getProperty, 1}},
- {3361, {wxStyledTextCtrl, getStyleBitsNeeded, 0}},
- {3362, {wxStyledTextCtrl, getCurrentLine, 0}},
- {3363, {wxStyledTextCtrl, styleSetSpec, 2}},
- {3364, {wxStyledTextCtrl, styleSetFont, 2}},
- {3365, {wxStyledTextCtrl, styleSetFontAttr, 7}},
- {3366, {wxStyledTextCtrl, styleSetCharacterSet, 2}},
- {3367, {wxStyledTextCtrl, styleSetFontEncoding, 2}},
- {3368, {wxStyledTextCtrl, cmdKeyExecute, 1}},
- {3369, {wxStyledTextCtrl, setMargins, 2}},
- {3370, {wxStyledTextCtrl, getSelection, 2}},
- {3371, {wxStyledTextCtrl, pointFromPosition, 1}},
- {3372, {wxStyledTextCtrl, scrollToLine, 1}},
- {3373, {wxStyledTextCtrl, scrollToColumn, 1}},
- {3374, {wxStyledTextCtrl, setVScrollBar, 1}},
- {3375, {wxStyledTextCtrl, setHScrollBar, 1}},
- {3376, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
- {3377, {wxStyledTextCtrl, setLastKeydownProcessed, 1}},
- {3378, {wxStyledTextCtrl, saveFile, 1}},
- {3379, {wxStyledTextCtrl, loadFile, 1}},
- {3380, {wxStyledTextCtrl, doDragOver, 3}},
- {3381, {wxStyledTextCtrl, doDropText, 3}},
- {3382, {wxStyledTextCtrl, getUseAntiAliasing, 0}},
- {3383, {wxStyledTextCtrl, addTextRaw, 1}},
- {3384, {wxStyledTextCtrl, insertTextRaw, 2}},
- {3385, {wxStyledTextCtrl, getCurLineRaw, 1}},
- {3386, {wxStyledTextCtrl, getLineRaw, 1}},
- {3387, {wxStyledTextCtrl, getSelectedTextRaw, 0}},
- {3388, {wxStyledTextCtrl, getTextRangeRaw, 2}},
- {3389, {wxStyledTextCtrl, setTextRaw, 1}},
- {3390, {wxStyledTextCtrl, getTextRaw, 0}},
- {3391, {wxStyledTextCtrl, appendTextRaw, 1}},
- {3392, {wxArtProvider, getBitmap, 2}},
- {3393, {wxArtProvider, getIcon, 2}},
- {3394, {wxTreeEvent, getKeyCode, 0}},
- {3395, {wxTreeEvent, getItem, 0}},
- {3396, {wxTreeEvent, getKeyEvent, 0}},
- {3397, {wxTreeEvent, getLabel, 0}},
- {3398, {wxTreeEvent, getOldItem, 0}},
- {3399, {wxTreeEvent, getPoint, 0}},
- {3400, {wxTreeEvent, isEditCancelled, 0}},
- {3401, {wxTreeEvent, setToolTip, 1}},
- {3402, {wxNotebookEvent, getOldSelection, 0}},
- {3403, {wxNotebookEvent, getSelection, 0}},
- {3404, {wxNotebookEvent, setOldSelection, 1}},
- {3405, {wxNotebookEvent, setSelection, 1}},
- {3406, {wxFileDataObject, new, 0}},
- {3407, {wxFileDataObject, addFile, 1}},
- {3408, {wxFileDataObject, getFilenames, 0}},
- {3409, {wxFileDataObject, 'Destroy', undefined}},
- {3410, {wxTextDataObject, new, 1}},
- {3411, {wxTextDataObject, getTextLength, 0}},
- {3412, {wxTextDataObject, getText, 0}},
- {3413, {wxTextDataObject, setText, 1}},
- {3414, {wxTextDataObject, 'Destroy', undefined}},
- {3415, {wxBitmapDataObject, new_1_1, 1}},
- {3416, {wxBitmapDataObject, new_1_0, 1}},
- {3417, {wxBitmapDataObject, getBitmap, 0}},
- {3418, {wxBitmapDataObject, setBitmap, 1}},
- {3419, {wxBitmapDataObject, 'Destroy', undefined}},
- {3421, {wxClipboard, new, 0}},
- {3422, {wxClipboard, destruct, 0}},
- {3423, {wxClipboard, addData, 1}},
- {3424, {wxClipboard, clear, 0}},
- {3425, {wxClipboard, close, 0}},
- {3426, {wxClipboard, flush, 0}},
- {3427, {wxClipboard, getData, 1}},
- {3428, {wxClipboard, isOpened, 0}},
- {3429, {wxClipboard, open, 0}},
- {3430, {wxClipboard, setData, 1}},
- {3432, {wxClipboard, usePrimarySelection, 1}},
- {3433, {wxClipboard, isSupported, 1}},
- {3434, {wxClipboard, get, 0}},
- {3435, {wxSpinEvent, getPosition, 0}},
- {3436, {wxSpinEvent, setPosition, 1}},
- {3437, {wxSplitterWindow, new_0, 0}},
- {3438, {wxSplitterWindow, new_2, 2}},
- {3439, {wxSplitterWindow, destruct, 0}},
- {3440, {wxSplitterWindow, create, 2}},
- {3441, {wxSplitterWindow, getMinimumPaneSize, 0}},
- {3442, {wxSplitterWindow, getSashGravity, 0}},
- {3443, {wxSplitterWindow, getSashPosition, 0}},
- {3444, {wxSplitterWindow, getSplitMode, 0}},
- {3445, {wxSplitterWindow, getWindow1, 0}},
- {3446, {wxSplitterWindow, getWindow2, 0}},
- {3447, {wxSplitterWindow, initialize, 1}},
- {3448, {wxSplitterWindow, isSplit, 0}},
- {3449, {wxSplitterWindow, replaceWindow, 2}},
- {3450, {wxSplitterWindow, setSashGravity, 1}},
- {3451, {wxSplitterWindow, setSashPosition, 2}},
- {3452, {wxSplitterWindow, setSashSize, 1}},
- {3453, {wxSplitterWindow, setMinimumPaneSize, 1}},
- {3454, {wxSplitterWindow, setSplitMode, 1}},
- {3455, {wxSplitterWindow, splitHorizontally, 3}},
- {3456, {wxSplitterWindow, splitVertically, 3}},
- {3457, {wxSplitterWindow, unsplit, 1}},
- {3458, {wxSplitterWindow, updateSize, 0}},
- {3459, {wxSplitterEvent, getSashPosition, 0}},
- {3460, {wxSplitterEvent, getX, 0}},
- {3461, {wxSplitterEvent, getY, 0}},
- {3462, {wxSplitterEvent, getWindowBeingRemoved, 0}},
- {3463, {wxSplitterEvent, setSashPosition, 1}},
- {3464, {wxHtmlWindow, new_0, 0}},
- {3465, {wxHtmlWindow, new_2, 2}},
- {3466, {wxHtmlWindow, appendToPage, 1}},
- {3467, {wxHtmlWindow, getOpenedAnchor, 0}},
- {3468, {wxHtmlWindow, getOpenedPage, 0}},
- {3469, {wxHtmlWindow, getOpenedPageTitle, 0}},
- {3470, {wxHtmlWindow, getRelatedFrame, 0}},
- {3471, {wxHtmlWindow, historyBack, 0}},
- {3472, {wxHtmlWindow, historyCanBack, 0}},
- {3473, {wxHtmlWindow, historyCanForward, 0}},
- {3474, {wxHtmlWindow, historyClear, 0}},
- {3475, {wxHtmlWindow, historyForward, 0}},
- {3476, {wxHtmlWindow, loadFile, 1}},
- {3477, {wxHtmlWindow, loadPage, 1}},
- {3478, {wxHtmlWindow, selectAll, 0}},
- {3479, {wxHtmlWindow, selectionToText, 0}},
- {3480, {wxHtmlWindow, selectLine, 1}},
- {3481, {wxHtmlWindow, selectWord, 1}},
- {3482, {wxHtmlWindow, setBorders, 1}},
- {3483, {wxHtmlWindow, setFonts, 3}},
- {3484, {wxHtmlWindow, setPage, 1}},
- {3485, {wxHtmlWindow, setRelatedFrame, 2}},
- {3486, {wxHtmlWindow, setRelatedStatusBar, 1}},
- {3487, {wxHtmlWindow, toText, 0}},
- {3488, {wxHtmlWindow, 'Destroy', undefined}},
- {3489, {wxHtmlLinkEvent, getLinkInfo, 0}},
- {3490, {wxSystemSettings, getColour, 1}},
- {3491, {wxSystemSettings, getFont, 1}},
- {3492, {wxSystemSettings, getMetric, 2}},
- {3493, {wxSystemSettings, getScreenType, 0}},
- {3494, {wxSystemOptions, getOption, 1}},
- {3495, {wxSystemOptions, getOptionInt, 1}},
- {3496, {wxSystemOptions, hasOption, 1}},
- {3497, {wxSystemOptions, isFalse, 1}},
- {3498, {wxSystemOptions, setOption_2_1, 2}},
- {3499, {wxSystemOptions, setOption_2_0, 2}},
- {3500, {wxAuiNotebookEvent, setSelection, 1}},
- {3501, {wxAuiNotebookEvent, getSelection, 0}},
- {3502, {wxAuiNotebookEvent, setOldSelection, 1}},
- {3503, {wxAuiNotebookEvent, getOldSelection, 0}},
- {3504, {wxAuiNotebookEvent, setDragSource, 1}},
- {3505, {wxAuiNotebookEvent, getDragSource, 0}},
- {3506, {wxAuiManagerEvent, setManager, 1}},
- {3507, {wxAuiManagerEvent, getManager, 0}},
- {3508, {wxAuiManagerEvent, setPane, 1}},
- {3509, {wxAuiManagerEvent, getPane, 0}},
- {3510, {wxAuiManagerEvent, setButton, 1}},
- {3511, {wxAuiManagerEvent, getButton, 0}},
- {3512, {wxAuiManagerEvent, setDC, 1}},
- {3513, {wxAuiManagerEvent, getDC, 0}},
- {3514, {wxAuiManagerEvent, veto, 1}},
- {3515, {wxAuiManagerEvent, getVeto, 0}},
- {3516, {wxAuiManagerEvent, setCanVeto, 1}},
- {3517, {wxAuiManagerEvent, canVeto, 0}},
- {3518, {wxLogNull, new, 0}},
- {3519, {wxLogNull, 'Destroy', undefined}},
- {3520, {wxTaskBarIcon, new, 0}},
- {3521, {wxTaskBarIcon, destruct, 0}},
- {3522, {wxTaskBarIcon, popupMenu, 1}},
- {3523, {wxTaskBarIcon, removeIcon, 0}},
- {3524, {wxTaskBarIcon, setIcon, 2}},
+ {2927, {gdicmn, displaySize, 2}},
+ {2928, {gdicmn, setCursor, 1}},
+ {2929, {wxPrintout, new, 1}},
+ {2930, {wxPrintout, destruct, 0}},
+ {2931, {wxPrintout, getDC, 0}},
+ {2932, {wxPrintout, getPageSizeMM, 2}},
+ {2933, {wxPrintout, getPageSizePixels, 2}},
+ {2934, {wxPrintout, getPaperRectPixels, 0}},
+ {2935, {wxPrintout, getPPIPrinter, 2}},
+ {2936, {wxPrintout, getPPIScreen, 2}},
+ {2937, {wxPrintout, getTitle, 0}},
+ {2938, {wxPrintout, isPreview, 0}},
+ {2939, {wxPrintout, fitThisSizeToPaper, 1}},
+ {2940, {wxPrintout, fitThisSizeToPage, 1}},
+ {2941, {wxPrintout, fitThisSizeToPageMargins, 2}},
+ {2942, {wxPrintout, mapScreenSizeToPaper, 0}},
+ {2943, {wxPrintout, mapScreenSizeToPage, 0}},
+ {2944, {wxPrintout, mapScreenSizeToPageMargins, 1}},
+ {2945, {wxPrintout, mapScreenSizeToDevice, 0}},
+ {2946, {wxPrintout, getLogicalPaperRect, 0}},
+ {2947, {wxPrintout, getLogicalPageRect, 0}},
+ {2948, {wxPrintout, getLogicalPageMarginsRect, 1}},
+ {2949, {wxPrintout, setLogicalOrigin, 2}},
+ {2950, {wxPrintout, offsetLogicalOrigin, 2}},
+ {2951, {wxStyledTextCtrl, new_2, 2}},
+ {2952, {wxStyledTextCtrl, new_0, 0}},
+ {2953, {wxStyledTextCtrl, destruct, 0}},
+ {2954, {wxStyledTextCtrl, create, 2}},
+ {2955, {wxStyledTextCtrl, addText, 1}},
+ {2956, {wxStyledTextCtrl, addStyledText, 1}},
+ {2957, {wxStyledTextCtrl, insertText, 2}},
+ {2958, {wxStyledTextCtrl, clearAll, 0}},
+ {2959, {wxStyledTextCtrl, clearDocumentStyle, 0}},
+ {2960, {wxStyledTextCtrl, getLength, 0}},
+ {2961, {wxStyledTextCtrl, getCharAt, 1}},
+ {2962, {wxStyledTextCtrl, getCurrentPos, 0}},
+ {2963, {wxStyledTextCtrl, getAnchor, 0}},
+ {2964, {wxStyledTextCtrl, getStyleAt, 1}},
+ {2965, {wxStyledTextCtrl, redo, 0}},
+ {2966, {wxStyledTextCtrl, setUndoCollection, 1}},
+ {2967, {wxStyledTextCtrl, selectAll, 0}},
+ {2968, {wxStyledTextCtrl, setSavePoint, 0}},
+ {2969, {wxStyledTextCtrl, getStyledText, 2}},
+ {2970, {wxStyledTextCtrl, canRedo, 0}},
+ {2971, {wxStyledTextCtrl, markerLineFromHandle, 1}},
+ {2972, {wxStyledTextCtrl, markerDeleteHandle, 1}},
+ {2973, {wxStyledTextCtrl, getUndoCollection, 0}},
+ {2974, {wxStyledTextCtrl, getViewWhiteSpace, 0}},
+ {2975, {wxStyledTextCtrl, setViewWhiteSpace, 1}},
+ {2976, {wxStyledTextCtrl, positionFromPoint, 1}},
+ {2977, {wxStyledTextCtrl, positionFromPointClose, 2}},
+ {2978, {wxStyledTextCtrl, gotoLine, 1}},
+ {2979, {wxStyledTextCtrl, gotoPos, 1}},
+ {2980, {wxStyledTextCtrl, setAnchor, 1}},
+ {2981, {wxStyledTextCtrl, getCurLine, 1}},
+ {2982, {wxStyledTextCtrl, getEndStyled, 0}},
+ {2983, {wxStyledTextCtrl, convertEOLs, 1}},
+ {2984, {wxStyledTextCtrl, getEOLMode, 0}},
+ {2985, {wxStyledTextCtrl, setEOLMode, 1}},
+ {2986, {wxStyledTextCtrl, startStyling, 2}},
+ {2987, {wxStyledTextCtrl, setStyling, 2}},
+ {2988, {wxStyledTextCtrl, getBufferedDraw, 0}},
+ {2989, {wxStyledTextCtrl, setBufferedDraw, 1}},
+ {2990, {wxStyledTextCtrl, setTabWidth, 1}},
+ {2991, {wxStyledTextCtrl, getTabWidth, 0}},
+ {2992, {wxStyledTextCtrl, setCodePage, 1}},
+ {2993, {wxStyledTextCtrl, markerDefine, 3}},
+ {2994, {wxStyledTextCtrl, markerSetForeground, 2}},
+ {2995, {wxStyledTextCtrl, markerSetBackground, 2}},
+ {2996, {wxStyledTextCtrl, markerAdd, 2}},
+ {2997, {wxStyledTextCtrl, markerDelete, 2}},
+ {2998, {wxStyledTextCtrl, markerDeleteAll, 1}},
+ {2999, {wxStyledTextCtrl, markerGet, 1}},
+ {3000, {wxStyledTextCtrl, markerNext, 2}},
+ {3001, {wxStyledTextCtrl, markerPrevious, 2}},
+ {3002, {wxStyledTextCtrl, markerDefineBitmap, 2}},
+ {3003, {wxStyledTextCtrl, markerAddSet, 2}},
+ {3004, {wxStyledTextCtrl, markerSetAlpha, 2}},
+ {3005, {wxStyledTextCtrl, setMarginType, 2}},
+ {3006, {wxStyledTextCtrl, getMarginType, 1}},
+ {3007, {wxStyledTextCtrl, setMarginWidth, 2}},
+ {3008, {wxStyledTextCtrl, getMarginWidth, 1}},
+ {3009, {wxStyledTextCtrl, setMarginMask, 2}},
+ {3010, {wxStyledTextCtrl, getMarginMask, 1}},
+ {3011, {wxStyledTextCtrl, setMarginSensitive, 2}},
+ {3012, {wxStyledTextCtrl, getMarginSensitive, 1}},
+ {3013, {wxStyledTextCtrl, styleClearAll, 0}},
+ {3014, {wxStyledTextCtrl, styleSetForeground, 2}},
+ {3015, {wxStyledTextCtrl, styleSetBackground, 2}},
+ {3016, {wxStyledTextCtrl, styleSetBold, 2}},
+ {3017, {wxStyledTextCtrl, styleSetItalic, 2}},
+ {3018, {wxStyledTextCtrl, styleSetSize, 2}},
+ {3019, {wxStyledTextCtrl, styleSetFaceName, 2}},
+ {3020, {wxStyledTextCtrl, styleSetEOLFilled, 2}},
+ {3021, {wxStyledTextCtrl, styleResetDefault, 0}},
+ {3022, {wxStyledTextCtrl, styleSetUnderline, 2}},
+ {3023, {wxStyledTextCtrl, styleSetCase, 2}},
+ {3024, {wxStyledTextCtrl, styleSetHotSpot, 2}},
+ {3025, {wxStyledTextCtrl, setSelForeground, 2}},
+ {3026, {wxStyledTextCtrl, setSelBackground, 2}},
+ {3027, {wxStyledTextCtrl, getSelAlpha, 0}},
+ {3028, {wxStyledTextCtrl, setSelAlpha, 1}},
+ {3029, {wxStyledTextCtrl, setCaretForeground, 1}},
+ {3030, {wxStyledTextCtrl, cmdKeyAssign, 3}},
+ {3031, {wxStyledTextCtrl, cmdKeyClear, 2}},
+ {3032, {wxStyledTextCtrl, cmdKeyClearAll, 0}},
+ {3033, {wxStyledTextCtrl, setStyleBytes, 2}},
+ {3034, {wxStyledTextCtrl, styleSetVisible, 2}},
+ {3035, {wxStyledTextCtrl, getCaretPeriod, 0}},
+ {3036, {wxStyledTextCtrl, setCaretPeriod, 1}},
+ {3037, {wxStyledTextCtrl, setWordChars, 1}},
+ {3038, {wxStyledTextCtrl, beginUndoAction, 0}},
+ {3039, {wxStyledTextCtrl, endUndoAction, 0}},
+ {3040, {wxStyledTextCtrl, indicatorSetStyle, 2}},
+ {3041, {wxStyledTextCtrl, indicatorGetStyle, 1}},
+ {3042, {wxStyledTextCtrl, indicatorSetForeground, 2}},
+ {3043, {wxStyledTextCtrl, indicatorGetForeground, 1}},
+ {3044, {wxStyledTextCtrl, setWhitespaceForeground, 2}},
+ {3045, {wxStyledTextCtrl, setWhitespaceBackground, 2}},
+ {3046, {wxStyledTextCtrl, getStyleBits, 0}},
+ {3047, {wxStyledTextCtrl, setLineState, 2}},
+ {3048, {wxStyledTextCtrl, getLineState, 1}},
+ {3049, {wxStyledTextCtrl, getMaxLineState, 0}},
+ {3050, {wxStyledTextCtrl, getCaretLineVisible, 0}},
+ {3051, {wxStyledTextCtrl, setCaretLineVisible, 1}},
+ {3052, {wxStyledTextCtrl, getCaretLineBackground, 0}},
+ {3053, {wxStyledTextCtrl, setCaretLineBackground, 1}},
+ {3054, {wxStyledTextCtrl, autoCompShow, 2}},
+ {3055, {wxStyledTextCtrl, autoCompCancel, 0}},
+ {3056, {wxStyledTextCtrl, autoCompActive, 0}},
+ {3057, {wxStyledTextCtrl, autoCompPosStart, 0}},
+ {3058, {wxStyledTextCtrl, autoCompComplete, 0}},
+ {3059, {wxStyledTextCtrl, autoCompStops, 1}},
+ {3060, {wxStyledTextCtrl, autoCompSetSeparator, 1}},
+ {3061, {wxStyledTextCtrl, autoCompGetSeparator, 0}},
+ {3062, {wxStyledTextCtrl, autoCompSelect, 1}},
+ {3063, {wxStyledTextCtrl, autoCompSetCancelAtStart, 1}},
+ {3064, {wxStyledTextCtrl, autoCompGetCancelAtStart, 0}},
+ {3065, {wxStyledTextCtrl, autoCompSetFillUps, 1}},
+ {3066, {wxStyledTextCtrl, autoCompSetChooseSingle, 1}},
+ {3067, {wxStyledTextCtrl, autoCompGetChooseSingle, 0}},
+ {3068, {wxStyledTextCtrl, autoCompSetIgnoreCase, 1}},
+ {3069, {wxStyledTextCtrl, autoCompGetIgnoreCase, 0}},
+ {3070, {wxStyledTextCtrl, userListShow, 2}},
+ {3071, {wxStyledTextCtrl, autoCompSetAutoHide, 1}},
+ {3072, {wxStyledTextCtrl, autoCompGetAutoHide, 0}},
+ {3073, {wxStyledTextCtrl, autoCompSetDropRestOfWord, 1}},
+ {3074, {wxStyledTextCtrl, autoCompGetDropRestOfWord, 0}},
+ {3075, {wxStyledTextCtrl, registerImage, 2}},
+ {3076, {wxStyledTextCtrl, clearRegisteredImages, 0}},
+ {3077, {wxStyledTextCtrl, autoCompGetTypeSeparator, 0}},
+ {3078, {wxStyledTextCtrl, autoCompSetTypeSeparator, 1}},
+ {3079, {wxStyledTextCtrl, autoCompSetMaxWidth, 1}},
+ {3080, {wxStyledTextCtrl, autoCompGetMaxWidth, 0}},
+ {3081, {wxStyledTextCtrl, autoCompSetMaxHeight, 1}},
+ {3082, {wxStyledTextCtrl, autoCompGetMaxHeight, 0}},
+ {3083, {wxStyledTextCtrl, setIndent, 1}},
+ {3084, {wxStyledTextCtrl, getIndent, 0}},
+ {3085, {wxStyledTextCtrl, setUseTabs, 1}},
+ {3086, {wxStyledTextCtrl, getUseTabs, 0}},
+ {3087, {wxStyledTextCtrl, setLineIndentation, 2}},
+ {3088, {wxStyledTextCtrl, getLineIndentation, 1}},
+ {3089, {wxStyledTextCtrl, getLineIndentPosition, 1}},
+ {3090, {wxStyledTextCtrl, getColumn, 1}},
+ {3091, {wxStyledTextCtrl, setUseHorizontalScrollBar, 1}},
+ {3092, {wxStyledTextCtrl, getUseHorizontalScrollBar, 0}},
+ {3093, {wxStyledTextCtrl, setIndentationGuides, 1}},
+ {3094, {wxStyledTextCtrl, getIndentationGuides, 0}},
+ {3095, {wxStyledTextCtrl, setHighlightGuide, 1}},
+ {3096, {wxStyledTextCtrl, getHighlightGuide, 0}},
+ {3097, {wxStyledTextCtrl, getLineEndPosition, 1}},
+ {3098, {wxStyledTextCtrl, getCodePage, 0}},
+ {3099, {wxStyledTextCtrl, getCaretForeground, 0}},
+ {3100, {wxStyledTextCtrl, getReadOnly, 0}},
+ {3101, {wxStyledTextCtrl, setCurrentPos, 1}},
+ {3102, {wxStyledTextCtrl, setSelectionStart, 1}},
+ {3103, {wxStyledTextCtrl, getSelectionStart, 0}},
+ {3104, {wxStyledTextCtrl, setSelectionEnd, 1}},
+ {3105, {wxStyledTextCtrl, getSelectionEnd, 0}},
+ {3106, {wxStyledTextCtrl, setPrintMagnification, 1}},
+ {3107, {wxStyledTextCtrl, getPrintMagnification, 0}},
+ {3108, {wxStyledTextCtrl, setPrintColourMode, 1}},
+ {3109, {wxStyledTextCtrl, getPrintColourMode, 0}},
+ {3110, {wxStyledTextCtrl, findText, 4}},
+ {3111, {wxStyledTextCtrl, formatRange, 7}},
+ {3112, {wxStyledTextCtrl, getFirstVisibleLine, 0}},
+ {3113, {wxStyledTextCtrl, getLine, 1}},
+ {3114, {wxStyledTextCtrl, getLineCount, 0}},
+ {3115, {wxStyledTextCtrl, setMarginLeft, 1}},
+ {3116, {wxStyledTextCtrl, getMarginLeft, 0}},
+ {3117, {wxStyledTextCtrl, setMarginRight, 1}},
+ {3118, {wxStyledTextCtrl, getMarginRight, 0}},
+ {3119, {wxStyledTextCtrl, getModify, 0}},
+ {3120, {wxStyledTextCtrl, setSelection, 2}},
+ {3121, {wxStyledTextCtrl, getSelectedText, 0}},
+ {3122, {wxStyledTextCtrl, getTextRange, 2}},
+ {3123, {wxStyledTextCtrl, hideSelection, 1}},
+ {3124, {wxStyledTextCtrl, lineFromPosition, 1}},
+ {3125, {wxStyledTextCtrl, positionFromLine, 1}},
+ {3126, {wxStyledTextCtrl, lineScroll, 2}},
+ {3127, {wxStyledTextCtrl, ensureCaretVisible, 0}},
+ {3128, {wxStyledTextCtrl, replaceSelection, 1}},
+ {3129, {wxStyledTextCtrl, setReadOnly, 1}},
+ {3130, {wxStyledTextCtrl, canPaste, 0}},
+ {3131, {wxStyledTextCtrl, canUndo, 0}},
+ {3132, {wxStyledTextCtrl, emptyUndoBuffer, 0}},
+ {3133, {wxStyledTextCtrl, undo, 0}},
+ {3134, {wxStyledTextCtrl, cut, 0}},
+ {3135, {wxStyledTextCtrl, copy, 0}},
+ {3136, {wxStyledTextCtrl, paste, 0}},
+ {3137, {wxStyledTextCtrl, clear, 0}},
+ {3138, {wxStyledTextCtrl, setText, 1}},
+ {3139, {wxStyledTextCtrl, getText, 0}},
+ {3140, {wxStyledTextCtrl, getTextLength, 0}},
+ {3141, {wxStyledTextCtrl, getOvertype, 0}},
+ {3142, {wxStyledTextCtrl, setCaretWidth, 1}},
+ {3143, {wxStyledTextCtrl, getCaretWidth, 0}},
+ {3144, {wxStyledTextCtrl, setTargetStart, 1}},
+ {3145, {wxStyledTextCtrl, getTargetStart, 0}},
+ {3146, {wxStyledTextCtrl, setTargetEnd, 1}},
+ {3147, {wxStyledTextCtrl, getTargetEnd, 0}},
+ {3148, {wxStyledTextCtrl, replaceTarget, 1}},
+ {3149, {wxStyledTextCtrl, searchInTarget, 1}},
+ {3150, {wxStyledTextCtrl, setSearchFlags, 1}},
+ {3151, {wxStyledTextCtrl, getSearchFlags, 0}},
+ {3152, {wxStyledTextCtrl, callTipShow, 2}},
+ {3153, {wxStyledTextCtrl, callTipCancel, 0}},
+ {3154, {wxStyledTextCtrl, callTipActive, 0}},
+ {3155, {wxStyledTextCtrl, callTipPosAtStart, 0}},
+ {3156, {wxStyledTextCtrl, callTipSetHighlight, 2}},
+ {3157, {wxStyledTextCtrl, callTipSetBackground, 1}},
+ {3158, {wxStyledTextCtrl, callTipSetForeground, 1}},
+ {3159, {wxStyledTextCtrl, callTipSetForegroundHighlight, 1}},
+ {3160, {wxStyledTextCtrl, callTipUseStyle, 1}},
+ {3161, {wxStyledTextCtrl, visibleFromDocLine, 1}},
+ {3162, {wxStyledTextCtrl, docLineFromVisible, 1}},
+ {3163, {wxStyledTextCtrl, wrapCount, 1}},
+ {3164, {wxStyledTextCtrl, setFoldLevel, 2}},
+ {3165, {wxStyledTextCtrl, getFoldLevel, 1}},
+ {3166, {wxStyledTextCtrl, getLastChild, 2}},
+ {3167, {wxStyledTextCtrl, getFoldParent, 1}},
+ {3168, {wxStyledTextCtrl, showLines, 2}},
+ {3169, {wxStyledTextCtrl, hideLines, 2}},
+ {3170, {wxStyledTextCtrl, getLineVisible, 1}},
+ {3171, {wxStyledTextCtrl, setFoldExpanded, 2}},
+ {3172, {wxStyledTextCtrl, getFoldExpanded, 1}},
+ {3173, {wxStyledTextCtrl, toggleFold, 1}},
+ {3174, {wxStyledTextCtrl, ensureVisible, 1}},
+ {3175, {wxStyledTextCtrl, setFoldFlags, 1}},
+ {3176, {wxStyledTextCtrl, ensureVisibleEnforcePolicy, 1}},
+ {3177, {wxStyledTextCtrl, setTabIndents, 1}},
+ {3178, {wxStyledTextCtrl, getTabIndents, 0}},
+ {3179, {wxStyledTextCtrl, setBackSpaceUnIndents, 1}},
+ {3180, {wxStyledTextCtrl, getBackSpaceUnIndents, 0}},
+ {3181, {wxStyledTextCtrl, setMouseDwellTime, 1}},
+ {3182, {wxStyledTextCtrl, getMouseDwellTime, 0}},
+ {3183, {wxStyledTextCtrl, wordStartPosition, 2}},
+ {3184, {wxStyledTextCtrl, wordEndPosition, 2}},
+ {3185, {wxStyledTextCtrl, setWrapMode, 1}},
+ {3186, {wxStyledTextCtrl, getWrapMode, 0}},
+ {3187, {wxStyledTextCtrl, setWrapVisualFlags, 1}},
+ {3188, {wxStyledTextCtrl, getWrapVisualFlags, 0}},
+ {3189, {wxStyledTextCtrl, setWrapVisualFlagsLocation, 1}},
+ {3190, {wxStyledTextCtrl, getWrapVisualFlagsLocation, 0}},
+ {3191, {wxStyledTextCtrl, setWrapStartIndent, 1}},
+ {3192, {wxStyledTextCtrl, getWrapStartIndent, 0}},
+ {3193, {wxStyledTextCtrl, setLayoutCache, 1}},
+ {3194, {wxStyledTextCtrl, getLayoutCache, 0}},
+ {3195, {wxStyledTextCtrl, setScrollWidth, 1}},
+ {3196, {wxStyledTextCtrl, getScrollWidth, 0}},
+ {3197, {wxStyledTextCtrl, textWidth, 2}},
+ {3198, {wxStyledTextCtrl, getEndAtLastLine, 0}},
+ {3199, {wxStyledTextCtrl, textHeight, 1}},
+ {3200, {wxStyledTextCtrl, setUseVerticalScrollBar, 1}},
+ {3201, {wxStyledTextCtrl, getUseVerticalScrollBar, 0}},
+ {3202, {wxStyledTextCtrl, appendText, 1}},
+ {3203, {wxStyledTextCtrl, getTwoPhaseDraw, 0}},
+ {3204, {wxStyledTextCtrl, setTwoPhaseDraw, 1}},
+ {3205, {wxStyledTextCtrl, targetFromSelection, 0}},
+ {3206, {wxStyledTextCtrl, linesJoin, 0}},
+ {3207, {wxStyledTextCtrl, linesSplit, 1}},
+ {3208, {wxStyledTextCtrl, setFoldMarginColour, 2}},
+ {3209, {wxStyledTextCtrl, setFoldMarginHiColour, 2}},
+ {3210, {wxStyledTextCtrl, lineDown, 0}},
+ {3211, {wxStyledTextCtrl, lineDownExtend, 0}},
+ {3212, {wxStyledTextCtrl, lineUp, 0}},
+ {3213, {wxStyledTextCtrl, lineUpExtend, 0}},
+ {3214, {wxStyledTextCtrl, charLeft, 0}},
+ {3215, {wxStyledTextCtrl, charLeftExtend, 0}},
+ {3216, {wxStyledTextCtrl, charRight, 0}},
+ {3217, {wxStyledTextCtrl, charRightExtend, 0}},
+ {3218, {wxStyledTextCtrl, wordLeft, 0}},
+ {3219, {wxStyledTextCtrl, wordLeftExtend, 0}},
+ {3220, {wxStyledTextCtrl, wordRight, 0}},
+ {3221, {wxStyledTextCtrl, wordRightExtend, 0}},
+ {3222, {wxStyledTextCtrl, home, 0}},
+ {3223, {wxStyledTextCtrl, homeExtend, 0}},
+ {3224, {wxStyledTextCtrl, lineEnd, 0}},
+ {3225, {wxStyledTextCtrl, lineEndExtend, 0}},
+ {3226, {wxStyledTextCtrl, documentStart, 0}},
+ {3227, {wxStyledTextCtrl, documentStartExtend, 0}},
+ {3228, {wxStyledTextCtrl, documentEnd, 0}},
+ {3229, {wxStyledTextCtrl, documentEndExtend, 0}},
+ {3230, {wxStyledTextCtrl, pageUp, 0}},
+ {3231, {wxStyledTextCtrl, pageUpExtend, 0}},
+ {3232, {wxStyledTextCtrl, pageDown, 0}},
+ {3233, {wxStyledTextCtrl, pageDownExtend, 0}},
+ {3234, {wxStyledTextCtrl, editToggleOvertype, 0}},
+ {3235, {wxStyledTextCtrl, cancel, 0}},
+ {3236, {wxStyledTextCtrl, deleteBack, 0}},
+ {3237, {wxStyledTextCtrl, tab, 0}},
+ {3238, {wxStyledTextCtrl, backTab, 0}},
+ {3239, {wxStyledTextCtrl, newLine, 0}},
+ {3240, {wxStyledTextCtrl, formFeed, 0}},
+ {3241, {wxStyledTextCtrl, vCHome, 0}},
+ {3242, {wxStyledTextCtrl, vCHomeExtend, 0}},
+ {3243, {wxStyledTextCtrl, zoomIn, 0}},
+ {3244, {wxStyledTextCtrl, zoomOut, 0}},
+ {3245, {wxStyledTextCtrl, delWordLeft, 0}},
+ {3246, {wxStyledTextCtrl, delWordRight, 0}},
+ {3247, {wxStyledTextCtrl, lineCut, 0}},
+ {3248, {wxStyledTextCtrl, lineDelete, 0}},
+ {3249, {wxStyledTextCtrl, lineTranspose, 0}},
+ {3250, {wxStyledTextCtrl, lineDuplicate, 0}},
+ {3251, {wxStyledTextCtrl, lowerCase, 0}},
+ {3252, {wxStyledTextCtrl, upperCase, 0}},
+ {3253, {wxStyledTextCtrl, lineScrollDown, 0}},
+ {3254, {wxStyledTextCtrl, lineScrollUp, 0}},
+ {3255, {wxStyledTextCtrl, deleteBackNotLine, 0}},
+ {3256, {wxStyledTextCtrl, homeDisplay, 0}},
+ {3257, {wxStyledTextCtrl, homeDisplayExtend, 0}},
+ {3258, {wxStyledTextCtrl, lineEndDisplay, 0}},
+ {3259, {wxStyledTextCtrl, lineEndDisplayExtend, 0}},
+ {3260, {wxStyledTextCtrl, homeWrapExtend, 0}},
+ {3261, {wxStyledTextCtrl, lineEndWrap, 0}},
+ {3262, {wxStyledTextCtrl, lineEndWrapExtend, 0}},
+ {3263, {wxStyledTextCtrl, vCHomeWrap, 0}},
+ {3264, {wxStyledTextCtrl, vCHomeWrapExtend, 0}},
+ {3265, {wxStyledTextCtrl, lineCopy, 0}},
+ {3266, {wxStyledTextCtrl, moveCaretInsideView, 0}},
+ {3267, {wxStyledTextCtrl, lineLength, 1}},
+ {3268, {wxStyledTextCtrl, braceHighlight, 2}},
+ {3269, {wxStyledTextCtrl, braceBadLight, 1}},
+ {3270, {wxStyledTextCtrl, braceMatch, 1}},
+ {3271, {wxStyledTextCtrl, getViewEOL, 0}},
+ {3272, {wxStyledTextCtrl, setViewEOL, 1}},
+ {3273, {wxStyledTextCtrl, setModEventMask, 1}},
+ {3274, {wxStyledTextCtrl, getEdgeColumn, 0}},
+ {3275, {wxStyledTextCtrl, setEdgeColumn, 1}},
+ {3276, {wxStyledTextCtrl, setEdgeMode, 1}},
+ {3277, {wxStyledTextCtrl, getEdgeMode, 0}},
+ {3278, {wxStyledTextCtrl, getEdgeColour, 0}},
+ {3279, {wxStyledTextCtrl, setEdgeColour, 1}},
+ {3280, {wxStyledTextCtrl, searchAnchor, 0}},
+ {3281, {wxStyledTextCtrl, searchNext, 2}},
+ {3282, {wxStyledTextCtrl, searchPrev, 2}},
+ {3283, {wxStyledTextCtrl, linesOnScreen, 0}},
+ {3284, {wxStyledTextCtrl, usePopUp, 1}},
+ {3285, {wxStyledTextCtrl, selectionIsRectangle, 0}},
+ {3286, {wxStyledTextCtrl, setZoom, 1}},
+ {3287, {wxStyledTextCtrl, getZoom, 0}},
+ {3288, {wxStyledTextCtrl, getModEventMask, 0}},
+ {3289, {wxStyledTextCtrl, setSTCFocus, 1}},
+ {3290, {wxStyledTextCtrl, getSTCFocus, 0}},
+ {3291, {wxStyledTextCtrl, setStatus, 1}},
+ {3292, {wxStyledTextCtrl, getStatus, 0}},
+ {3293, {wxStyledTextCtrl, setMouseDownCaptures, 1}},
+ {3294, {wxStyledTextCtrl, getMouseDownCaptures, 0}},
+ {3295, {wxStyledTextCtrl, setSTCCursor, 1}},
+ {3296, {wxStyledTextCtrl, getSTCCursor, 0}},
+ {3297, {wxStyledTextCtrl, setControlCharSymbol, 1}},
+ {3298, {wxStyledTextCtrl, getControlCharSymbol, 0}},
+ {3299, {wxStyledTextCtrl, wordPartLeft, 0}},
+ {3300, {wxStyledTextCtrl, wordPartLeftExtend, 0}},
+ {3301, {wxStyledTextCtrl, wordPartRight, 0}},
+ {3302, {wxStyledTextCtrl, wordPartRightExtend, 0}},
+ {3303, {wxStyledTextCtrl, setVisiblePolicy, 2}},
+ {3304, {wxStyledTextCtrl, delLineLeft, 0}},
+ {3305, {wxStyledTextCtrl, delLineRight, 0}},
+ {3306, {wxStyledTextCtrl, getXOffset, 0}},
+ {3307, {wxStyledTextCtrl, chooseCaretX, 0}},
+ {3308, {wxStyledTextCtrl, setXCaretPolicy, 2}},
+ {3309, {wxStyledTextCtrl, setYCaretPolicy, 2}},
+ {3310, {wxStyledTextCtrl, getPrintWrapMode, 0}},
+ {3311, {wxStyledTextCtrl, setHotspotActiveForeground, 2}},
+ {3312, {wxStyledTextCtrl, setHotspotActiveBackground, 2}},
+ {3313, {wxStyledTextCtrl, setHotspotActiveUnderline, 1}},
+ {3314, {wxStyledTextCtrl, setHotspotSingleLine, 1}},
+ {3315, {wxStyledTextCtrl, paraDownExtend, 0}},
+ {3316, {wxStyledTextCtrl, paraUp, 0}},
+ {3317, {wxStyledTextCtrl, paraUpExtend, 0}},
+ {3318, {wxStyledTextCtrl, positionBefore, 1}},
+ {3319, {wxStyledTextCtrl, positionAfter, 1}},
+ {3320, {wxStyledTextCtrl, copyRange, 2}},
+ {3321, {wxStyledTextCtrl, copyText, 2}},
+ {3322, {wxStyledTextCtrl, setSelectionMode, 1}},
+ {3323, {wxStyledTextCtrl, getSelectionMode, 0}},
+ {3324, {wxStyledTextCtrl, lineDownRectExtend, 0}},
+ {3325, {wxStyledTextCtrl, lineUpRectExtend, 0}},
+ {3326, {wxStyledTextCtrl, charLeftRectExtend, 0}},
+ {3327, {wxStyledTextCtrl, charRightRectExtend, 0}},
+ {3328, {wxStyledTextCtrl, homeRectExtend, 0}},
+ {3329, {wxStyledTextCtrl, vCHomeRectExtend, 0}},
+ {3330, {wxStyledTextCtrl, lineEndRectExtend, 0}},
+ {3331, {wxStyledTextCtrl, pageUpRectExtend, 0}},
+ {3332, {wxStyledTextCtrl, pageDownRectExtend, 0}},
+ {3333, {wxStyledTextCtrl, stutteredPageUp, 0}},
+ {3334, {wxStyledTextCtrl, stutteredPageUpExtend, 0}},
+ {3335, {wxStyledTextCtrl, stutteredPageDown, 0}},
+ {3336, {wxStyledTextCtrl, stutteredPageDownExtend, 0}},
+ {3337, {wxStyledTextCtrl, wordLeftEnd, 0}},
+ {3338, {wxStyledTextCtrl, wordLeftEndExtend, 0}},
+ {3339, {wxStyledTextCtrl, wordRightEnd, 0}},
+ {3340, {wxStyledTextCtrl, wordRightEndExtend, 0}},
+ {3341, {wxStyledTextCtrl, setWhitespaceChars, 1}},
+ {3342, {wxStyledTextCtrl, setCharsDefault, 0}},
+ {3343, {wxStyledTextCtrl, autoCompGetCurrent, 0}},
+ {3344, {wxStyledTextCtrl, allocate, 1}},
+ {3345, {wxStyledTextCtrl, findColumn, 2}},
+ {3346, {wxStyledTextCtrl, getCaretSticky, 0}},
+ {3347, {wxStyledTextCtrl, setCaretSticky, 1}},
+ {3348, {wxStyledTextCtrl, toggleCaretSticky, 0}},
+ {3349, {wxStyledTextCtrl, setPasteConvertEndings, 1}},
+ {3350, {wxStyledTextCtrl, getPasteConvertEndings, 0}},
+ {3351, {wxStyledTextCtrl, selectionDuplicate, 0}},
+ {3352, {wxStyledTextCtrl, setCaretLineBackAlpha, 1}},
+ {3353, {wxStyledTextCtrl, getCaretLineBackAlpha, 0}},
+ {3354, {wxStyledTextCtrl, startRecord, 0}},
+ {3355, {wxStyledTextCtrl, stopRecord, 0}},
+ {3356, {wxStyledTextCtrl, setLexer, 1}},
+ {3357, {wxStyledTextCtrl, getLexer, 0}},
+ {3358, {wxStyledTextCtrl, colourise, 2}},
+ {3359, {wxStyledTextCtrl, setProperty, 2}},
+ {3360, {wxStyledTextCtrl, setKeyWords, 2}},
+ {3361, {wxStyledTextCtrl, setLexerLanguage, 1}},
+ {3362, {wxStyledTextCtrl, getProperty, 1}},
+ {3363, {wxStyledTextCtrl, getStyleBitsNeeded, 0}},
+ {3364, {wxStyledTextCtrl, getCurrentLine, 0}},
+ {3365, {wxStyledTextCtrl, styleSetSpec, 2}},
+ {3366, {wxStyledTextCtrl, styleSetFont, 2}},
+ {3367, {wxStyledTextCtrl, styleSetFontAttr, 7}},
+ {3368, {wxStyledTextCtrl, styleSetCharacterSet, 2}},
+ {3369, {wxStyledTextCtrl, styleSetFontEncoding, 2}},
+ {3370, {wxStyledTextCtrl, cmdKeyExecute, 1}},
+ {3371, {wxStyledTextCtrl, setMargins, 2}},
+ {3372, {wxStyledTextCtrl, getSelection, 2}},
+ {3373, {wxStyledTextCtrl, pointFromPosition, 1}},
+ {3374, {wxStyledTextCtrl, scrollToLine, 1}},
+ {3375, {wxStyledTextCtrl, scrollToColumn, 1}},
+ {3376, {wxStyledTextCtrl, setVScrollBar, 1}},
+ {3377, {wxStyledTextCtrl, setHScrollBar, 1}},
+ {3378, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
+ {3379, {wxStyledTextCtrl, setLastKeydownProcessed, 1}},
+ {3380, {wxStyledTextCtrl, saveFile, 1}},
+ {3381, {wxStyledTextCtrl, loadFile, 1}},
+ {3382, {wxStyledTextCtrl, doDragOver, 3}},
+ {3383, {wxStyledTextCtrl, doDropText, 3}},
+ {3384, {wxStyledTextCtrl, getUseAntiAliasing, 0}},
+ {3385, {wxStyledTextCtrl, addTextRaw, 1}},
+ {3386, {wxStyledTextCtrl, insertTextRaw, 2}},
+ {3387, {wxStyledTextCtrl, getCurLineRaw, 1}},
+ {3388, {wxStyledTextCtrl, getLineRaw, 1}},
+ {3389, {wxStyledTextCtrl, getSelectedTextRaw, 0}},
+ {3390, {wxStyledTextCtrl, getTextRangeRaw, 2}},
+ {3391, {wxStyledTextCtrl, setTextRaw, 1}},
+ {3392, {wxStyledTextCtrl, getTextRaw, 0}},
+ {3393, {wxStyledTextCtrl, appendTextRaw, 1}},
+ {3394, {wxArtProvider, getBitmap, 2}},
+ {3395, {wxArtProvider, getIcon, 2}},
+ {3396, {wxTreeEvent, getKeyCode, 0}},
+ {3397, {wxTreeEvent, getItem, 0}},
+ {3398, {wxTreeEvent, getKeyEvent, 0}},
+ {3399, {wxTreeEvent, getLabel, 0}},
+ {3400, {wxTreeEvent, getOldItem, 0}},
+ {3401, {wxTreeEvent, getPoint, 0}},
+ {3402, {wxTreeEvent, isEditCancelled, 0}},
+ {3403, {wxTreeEvent, setToolTip, 1}},
+ {3404, {wxNotebookEvent, getOldSelection, 0}},
+ {3405, {wxNotebookEvent, getSelection, 0}},
+ {3406, {wxNotebookEvent, setOldSelection, 1}},
+ {3407, {wxNotebookEvent, setSelection, 1}},
+ {3408, {wxFileDataObject, new, 0}},
+ {3409, {wxFileDataObject, addFile, 1}},
+ {3410, {wxFileDataObject, getFilenames, 0}},
+ {3411, {wxFileDataObject, 'Destroy', undefined}},
+ {3412, {wxTextDataObject, new, 1}},
+ {3413, {wxTextDataObject, getTextLength, 0}},
+ {3414, {wxTextDataObject, getText, 0}},
+ {3415, {wxTextDataObject, setText, 1}},
+ {3416, {wxTextDataObject, 'Destroy', undefined}},
+ {3417, {wxBitmapDataObject, new_1_1, 1}},
+ {3418, {wxBitmapDataObject, new_1_0, 1}},
+ {3419, {wxBitmapDataObject, getBitmap, 0}},
+ {3420, {wxBitmapDataObject, setBitmap, 1}},
+ {3421, {wxBitmapDataObject, 'Destroy', undefined}},
+ {3423, {wxClipboard, new, 0}},
+ {3424, {wxClipboard, destruct, 0}},
+ {3425, {wxClipboard, addData, 1}},
+ {3426, {wxClipboard, clear, 0}},
+ {3427, {wxClipboard, close, 0}},
+ {3428, {wxClipboard, flush, 0}},
+ {3429, {wxClipboard, getData, 1}},
+ {3430, {wxClipboard, isOpened, 0}},
+ {3431, {wxClipboard, open, 0}},
+ {3432, {wxClipboard, setData, 1}},
+ {3434, {wxClipboard, usePrimarySelection, 1}},
+ {3435, {wxClipboard, isSupported, 1}},
+ {3436, {wxClipboard, get, 0}},
+ {3437, {wxSpinEvent, getPosition, 0}},
+ {3438, {wxSpinEvent, setPosition, 1}},
+ {3439, {wxSplitterWindow, new_0, 0}},
+ {3440, {wxSplitterWindow, new_2, 2}},
+ {3441, {wxSplitterWindow, destruct, 0}},
+ {3442, {wxSplitterWindow, create, 2}},
+ {3443, {wxSplitterWindow, getMinimumPaneSize, 0}},
+ {3444, {wxSplitterWindow, getSashGravity, 0}},
+ {3445, {wxSplitterWindow, getSashPosition, 0}},
+ {3446, {wxSplitterWindow, getSplitMode, 0}},
+ {3447, {wxSplitterWindow, getWindow1, 0}},
+ {3448, {wxSplitterWindow, getWindow2, 0}},
+ {3449, {wxSplitterWindow, initialize, 1}},
+ {3450, {wxSplitterWindow, isSplit, 0}},
+ {3451, {wxSplitterWindow, replaceWindow, 2}},
+ {3452, {wxSplitterWindow, setSashGravity, 1}},
+ {3453, {wxSplitterWindow, setSashPosition, 2}},
+ {3454, {wxSplitterWindow, setSashSize, 1}},
+ {3455, {wxSplitterWindow, setMinimumPaneSize, 1}},
+ {3456, {wxSplitterWindow, setSplitMode, 1}},
+ {3457, {wxSplitterWindow, splitHorizontally, 3}},
+ {3458, {wxSplitterWindow, splitVertically, 3}},
+ {3459, {wxSplitterWindow, unsplit, 1}},
+ {3460, {wxSplitterWindow, updateSize, 0}},
+ {3461, {wxSplitterEvent, getSashPosition, 0}},
+ {3462, {wxSplitterEvent, getX, 0}},
+ {3463, {wxSplitterEvent, getY, 0}},
+ {3464, {wxSplitterEvent, getWindowBeingRemoved, 0}},
+ {3465, {wxSplitterEvent, setSashPosition, 1}},
+ {3466, {wxHtmlWindow, new_0, 0}},
+ {3467, {wxHtmlWindow, new_2, 2}},
+ {3468, {wxHtmlWindow, appendToPage, 1}},
+ {3469, {wxHtmlWindow, getOpenedAnchor, 0}},
+ {3470, {wxHtmlWindow, getOpenedPage, 0}},
+ {3471, {wxHtmlWindow, getOpenedPageTitle, 0}},
+ {3472, {wxHtmlWindow, getRelatedFrame, 0}},
+ {3473, {wxHtmlWindow, historyBack, 0}},
+ {3474, {wxHtmlWindow, historyCanBack, 0}},
+ {3475, {wxHtmlWindow, historyCanForward, 0}},
+ {3476, {wxHtmlWindow, historyClear, 0}},
+ {3477, {wxHtmlWindow, historyForward, 0}},
+ {3478, {wxHtmlWindow, loadFile, 1}},
+ {3479, {wxHtmlWindow, loadPage, 1}},
+ {3480, {wxHtmlWindow, selectAll, 0}},
+ {3481, {wxHtmlWindow, selectionToText, 0}},
+ {3482, {wxHtmlWindow, selectLine, 1}},
+ {3483, {wxHtmlWindow, selectWord, 1}},
+ {3484, {wxHtmlWindow, setBorders, 1}},
+ {3485, {wxHtmlWindow, setFonts, 3}},
+ {3486, {wxHtmlWindow, setPage, 1}},
+ {3487, {wxHtmlWindow, setRelatedFrame, 2}},
+ {3488, {wxHtmlWindow, setRelatedStatusBar, 1}},
+ {3489, {wxHtmlWindow, toText, 0}},
+ {3490, {wxHtmlWindow, 'Destroy', undefined}},
+ {3491, {wxHtmlLinkEvent, getLinkInfo, 0}},
+ {3492, {wxSystemSettings, getColour, 1}},
+ {3493, {wxSystemSettings, getFont, 1}},
+ {3494, {wxSystemSettings, getMetric, 2}},
+ {3495, {wxSystemSettings, getScreenType, 0}},
+ {3496, {wxSystemOptions, getOption, 1}},
+ {3497, {wxSystemOptions, getOptionInt, 1}},
+ {3498, {wxSystemOptions, hasOption, 1}},
+ {3499, {wxSystemOptions, isFalse, 1}},
+ {3500, {wxSystemOptions, setOption_2_1, 2}},
+ {3501, {wxSystemOptions, setOption_2_0, 2}},
+ {3502, {wxAuiNotebookEvent, setSelection, 1}},
+ {3503, {wxAuiNotebookEvent, getSelection, 0}},
+ {3504, {wxAuiNotebookEvent, setOldSelection, 1}},
+ {3505, {wxAuiNotebookEvent, getOldSelection, 0}},
+ {3506, {wxAuiNotebookEvent, setDragSource, 1}},
+ {3507, {wxAuiNotebookEvent, getDragSource, 0}},
+ {3508, {wxAuiManagerEvent, setManager, 1}},
+ {3509, {wxAuiManagerEvent, getManager, 0}},
+ {3510, {wxAuiManagerEvent, setPane, 1}},
+ {3511, {wxAuiManagerEvent, getPane, 0}},
+ {3512, {wxAuiManagerEvent, setButton, 1}},
+ {3513, {wxAuiManagerEvent, getButton, 0}},
+ {3514, {wxAuiManagerEvent, setDC, 1}},
+ {3515, {wxAuiManagerEvent, getDC, 0}},
+ {3516, {wxAuiManagerEvent, veto, 1}},
+ {3517, {wxAuiManagerEvent, getVeto, 0}},
+ {3518, {wxAuiManagerEvent, setCanVeto, 1}},
+ {3519, {wxAuiManagerEvent, canVeto, 0}},
+ {3520, {wxLogNull, new, 0}},
+ {3521, {wxLogNull, 'Destroy', undefined}},
+ {3522, {wxTaskBarIcon, new, 0}},
+ {3523, {wxTaskBarIcon, destruct, 0}},
+ {3524, {wxTaskBarIcon, popupMenu, 1}},
+ {3525, {wxTaskBarIcon, removeIcon, 0}},
+ {3526, {wxTaskBarIcon, setIcon, 2}},
+ {3527, {wxLocale, new_0, 0}},
+ {3529, {wxLocale, new_2, 2}},
+ {3530, {wxLocale, destruct, 0}},
+ {3532, {wxLocale, init, 1}},
+ {3533, {wxLocale, addCatalog_1, 1}},
+ {3534, {wxLocale, addCatalog_3, 3}},
+ {3535, {wxLocale, addCatalogLookupPathPrefix, 1}},
+ {3536, {wxLocale, getCanonicalName, 0}},
+ {3537, {wxLocale, getLanguage, 0}},
+ {3538, {wxLocale, getLanguageName, 1}},
+ {3539, {wxLocale, getLocale, 0}},
+ {3540, {wxLocale, getName, 0}},
+ {3541, {wxLocale, getString_2, 2}},
+ {3542, {wxLocale, getString_4, 4}},
+ {3543, {wxLocale, getHeaderValue, 2}},
+ {3544, {wxLocale, getSysName, 0}},
+ {3545, {wxLocale, getSystemEncoding, 0}},
+ {3546, {wxLocale, getSystemEncodingName, 0}},
+ {3547, {wxLocale, getSystemLanguage, 0}},
+ {3548, {wxLocale, isLoaded, 1}},
+ {3549, {wxLocale, isOk, 0}},
{-1, {mod, func, -1}}
].
diff --git a/lib/wx/src/gen/wxe_funcs.hrl b/lib/wx/src/gen/wxe_funcs.hrl
index 109368adb3..97f9bd9695 100644
--- a/lib/wx/src/gen/wxe_funcs.hrl
+++ b/lib/wx/src/gen/wxe_funcs.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -2706,599 +2706,622 @@
-define(utils_wxGetOsDescription, 2924).
-define(utils_wxIsPlatformLittleEndian, 2925).
-define(utils_wxIsPlatform64Bit, 2926).
--define(wxPrintout_new, 2927).
--define(wxPrintout_destruct, 2928).
--define(wxPrintout_GetDC, 2929).
--define(wxPrintout_GetPageSizeMM, 2930).
--define(wxPrintout_GetPageSizePixels, 2931).
--define(wxPrintout_GetPaperRectPixels, 2932).
--define(wxPrintout_GetPPIPrinter, 2933).
--define(wxPrintout_GetPPIScreen, 2934).
--define(wxPrintout_GetTitle, 2935).
--define(wxPrintout_IsPreview, 2936).
--define(wxPrintout_FitThisSizeToPaper, 2937).
--define(wxPrintout_FitThisSizeToPage, 2938).
--define(wxPrintout_FitThisSizeToPageMargins, 2939).
--define(wxPrintout_MapScreenSizeToPaper, 2940).
--define(wxPrintout_MapScreenSizeToPage, 2941).
--define(wxPrintout_MapScreenSizeToPageMargins, 2942).
--define(wxPrintout_MapScreenSizeToDevice, 2943).
--define(wxPrintout_GetLogicalPaperRect, 2944).
--define(wxPrintout_GetLogicalPageRect, 2945).
--define(wxPrintout_GetLogicalPageMarginsRect, 2946).
--define(wxPrintout_SetLogicalOrigin, 2947).
--define(wxPrintout_OffsetLogicalOrigin, 2948).
--define(wxStyledTextCtrl_new_2, 2949).
--define(wxStyledTextCtrl_new_0, 2950).
--define(wxStyledTextCtrl_destruct, 2951).
--define(wxStyledTextCtrl_Create, 2952).
--define(wxStyledTextCtrl_AddText, 2953).
--define(wxStyledTextCtrl_AddStyledText, 2954).
--define(wxStyledTextCtrl_InsertText, 2955).
--define(wxStyledTextCtrl_ClearAll, 2956).
--define(wxStyledTextCtrl_ClearDocumentStyle, 2957).
--define(wxStyledTextCtrl_GetLength, 2958).
--define(wxStyledTextCtrl_GetCharAt, 2959).
--define(wxStyledTextCtrl_GetCurrentPos, 2960).
--define(wxStyledTextCtrl_GetAnchor, 2961).
--define(wxStyledTextCtrl_GetStyleAt, 2962).
--define(wxStyledTextCtrl_Redo, 2963).
--define(wxStyledTextCtrl_SetUndoCollection, 2964).
--define(wxStyledTextCtrl_SelectAll, 2965).
--define(wxStyledTextCtrl_SetSavePoint, 2966).
--define(wxStyledTextCtrl_GetStyledText, 2967).
--define(wxStyledTextCtrl_CanRedo, 2968).
--define(wxStyledTextCtrl_MarkerLineFromHandle, 2969).
--define(wxStyledTextCtrl_MarkerDeleteHandle, 2970).
--define(wxStyledTextCtrl_GetUndoCollection, 2971).
--define(wxStyledTextCtrl_GetViewWhiteSpace, 2972).
--define(wxStyledTextCtrl_SetViewWhiteSpace, 2973).
--define(wxStyledTextCtrl_PositionFromPoint, 2974).
--define(wxStyledTextCtrl_PositionFromPointClose, 2975).
--define(wxStyledTextCtrl_GotoLine, 2976).
--define(wxStyledTextCtrl_GotoPos, 2977).
--define(wxStyledTextCtrl_SetAnchor, 2978).
--define(wxStyledTextCtrl_GetCurLine, 2979).
--define(wxStyledTextCtrl_GetEndStyled, 2980).
--define(wxStyledTextCtrl_ConvertEOLs, 2981).
--define(wxStyledTextCtrl_GetEOLMode, 2982).
--define(wxStyledTextCtrl_SetEOLMode, 2983).
--define(wxStyledTextCtrl_StartStyling, 2984).
--define(wxStyledTextCtrl_SetStyling, 2985).
--define(wxStyledTextCtrl_GetBufferedDraw, 2986).
--define(wxStyledTextCtrl_SetBufferedDraw, 2987).
--define(wxStyledTextCtrl_SetTabWidth, 2988).
--define(wxStyledTextCtrl_GetTabWidth, 2989).
--define(wxStyledTextCtrl_SetCodePage, 2990).
--define(wxStyledTextCtrl_MarkerDefine, 2991).
--define(wxStyledTextCtrl_MarkerSetForeground, 2992).
--define(wxStyledTextCtrl_MarkerSetBackground, 2993).
--define(wxStyledTextCtrl_MarkerAdd, 2994).
--define(wxStyledTextCtrl_MarkerDelete, 2995).
--define(wxStyledTextCtrl_MarkerDeleteAll, 2996).
--define(wxStyledTextCtrl_MarkerGet, 2997).
--define(wxStyledTextCtrl_MarkerNext, 2998).
--define(wxStyledTextCtrl_MarkerPrevious, 2999).
--define(wxStyledTextCtrl_MarkerDefineBitmap, 3000).
--define(wxStyledTextCtrl_MarkerAddSet, 3001).
--define(wxStyledTextCtrl_MarkerSetAlpha, 3002).
--define(wxStyledTextCtrl_SetMarginType, 3003).
--define(wxStyledTextCtrl_GetMarginType, 3004).
--define(wxStyledTextCtrl_SetMarginWidth, 3005).
--define(wxStyledTextCtrl_GetMarginWidth, 3006).
--define(wxStyledTextCtrl_SetMarginMask, 3007).
--define(wxStyledTextCtrl_GetMarginMask, 3008).
--define(wxStyledTextCtrl_SetMarginSensitive, 3009).
--define(wxStyledTextCtrl_GetMarginSensitive, 3010).
--define(wxStyledTextCtrl_StyleClearAll, 3011).
--define(wxStyledTextCtrl_StyleSetForeground, 3012).
--define(wxStyledTextCtrl_StyleSetBackground, 3013).
--define(wxStyledTextCtrl_StyleSetBold, 3014).
--define(wxStyledTextCtrl_StyleSetItalic, 3015).
--define(wxStyledTextCtrl_StyleSetSize, 3016).
--define(wxStyledTextCtrl_StyleSetFaceName, 3017).
--define(wxStyledTextCtrl_StyleSetEOLFilled, 3018).
--define(wxStyledTextCtrl_StyleResetDefault, 3019).
--define(wxStyledTextCtrl_StyleSetUnderline, 3020).
--define(wxStyledTextCtrl_StyleSetCase, 3021).
--define(wxStyledTextCtrl_StyleSetHotSpot, 3022).
--define(wxStyledTextCtrl_SetSelForeground, 3023).
--define(wxStyledTextCtrl_SetSelBackground, 3024).
--define(wxStyledTextCtrl_GetSelAlpha, 3025).
--define(wxStyledTextCtrl_SetSelAlpha, 3026).
--define(wxStyledTextCtrl_SetCaretForeground, 3027).
--define(wxStyledTextCtrl_CmdKeyAssign, 3028).
--define(wxStyledTextCtrl_CmdKeyClear, 3029).
--define(wxStyledTextCtrl_CmdKeyClearAll, 3030).
--define(wxStyledTextCtrl_SetStyleBytes, 3031).
--define(wxStyledTextCtrl_StyleSetVisible, 3032).
--define(wxStyledTextCtrl_GetCaretPeriod, 3033).
--define(wxStyledTextCtrl_SetCaretPeriod, 3034).
--define(wxStyledTextCtrl_SetWordChars, 3035).
--define(wxStyledTextCtrl_BeginUndoAction, 3036).
--define(wxStyledTextCtrl_EndUndoAction, 3037).
--define(wxStyledTextCtrl_IndicatorSetStyle, 3038).
--define(wxStyledTextCtrl_IndicatorGetStyle, 3039).
--define(wxStyledTextCtrl_IndicatorSetForeground, 3040).
--define(wxStyledTextCtrl_IndicatorGetForeground, 3041).
--define(wxStyledTextCtrl_SetWhitespaceForeground, 3042).
--define(wxStyledTextCtrl_SetWhitespaceBackground, 3043).
--define(wxStyledTextCtrl_GetStyleBits, 3044).
--define(wxStyledTextCtrl_SetLineState, 3045).
--define(wxStyledTextCtrl_GetLineState, 3046).
--define(wxStyledTextCtrl_GetMaxLineState, 3047).
--define(wxStyledTextCtrl_GetCaretLineVisible, 3048).
--define(wxStyledTextCtrl_SetCaretLineVisible, 3049).
--define(wxStyledTextCtrl_GetCaretLineBackground, 3050).
--define(wxStyledTextCtrl_SetCaretLineBackground, 3051).
--define(wxStyledTextCtrl_AutoCompShow, 3052).
--define(wxStyledTextCtrl_AutoCompCancel, 3053).
--define(wxStyledTextCtrl_AutoCompActive, 3054).
--define(wxStyledTextCtrl_AutoCompPosStart, 3055).
--define(wxStyledTextCtrl_AutoCompComplete, 3056).
--define(wxStyledTextCtrl_AutoCompStops, 3057).
--define(wxStyledTextCtrl_AutoCompSetSeparator, 3058).
--define(wxStyledTextCtrl_AutoCompGetSeparator, 3059).
--define(wxStyledTextCtrl_AutoCompSelect, 3060).
--define(wxStyledTextCtrl_AutoCompSetCancelAtStart, 3061).
--define(wxStyledTextCtrl_AutoCompGetCancelAtStart, 3062).
--define(wxStyledTextCtrl_AutoCompSetFillUps, 3063).
--define(wxStyledTextCtrl_AutoCompSetChooseSingle, 3064).
--define(wxStyledTextCtrl_AutoCompGetChooseSingle, 3065).
--define(wxStyledTextCtrl_AutoCompSetIgnoreCase, 3066).
--define(wxStyledTextCtrl_AutoCompGetIgnoreCase, 3067).
--define(wxStyledTextCtrl_UserListShow, 3068).
--define(wxStyledTextCtrl_AutoCompSetAutoHide, 3069).
--define(wxStyledTextCtrl_AutoCompGetAutoHide, 3070).
--define(wxStyledTextCtrl_AutoCompSetDropRestOfWord, 3071).
--define(wxStyledTextCtrl_AutoCompGetDropRestOfWord, 3072).
--define(wxStyledTextCtrl_RegisterImage, 3073).
--define(wxStyledTextCtrl_ClearRegisteredImages, 3074).
--define(wxStyledTextCtrl_AutoCompGetTypeSeparator, 3075).
--define(wxStyledTextCtrl_AutoCompSetTypeSeparator, 3076).
--define(wxStyledTextCtrl_AutoCompSetMaxWidth, 3077).
--define(wxStyledTextCtrl_AutoCompGetMaxWidth, 3078).
--define(wxStyledTextCtrl_AutoCompSetMaxHeight, 3079).
--define(wxStyledTextCtrl_AutoCompGetMaxHeight, 3080).
--define(wxStyledTextCtrl_SetIndent, 3081).
--define(wxStyledTextCtrl_GetIndent, 3082).
--define(wxStyledTextCtrl_SetUseTabs, 3083).
--define(wxStyledTextCtrl_GetUseTabs, 3084).
--define(wxStyledTextCtrl_SetLineIndentation, 3085).
--define(wxStyledTextCtrl_GetLineIndentation, 3086).
--define(wxStyledTextCtrl_GetLineIndentPosition, 3087).
--define(wxStyledTextCtrl_GetColumn, 3088).
--define(wxStyledTextCtrl_SetUseHorizontalScrollBar, 3089).
--define(wxStyledTextCtrl_GetUseHorizontalScrollBar, 3090).
--define(wxStyledTextCtrl_SetIndentationGuides, 3091).
--define(wxStyledTextCtrl_GetIndentationGuides, 3092).
--define(wxStyledTextCtrl_SetHighlightGuide, 3093).
--define(wxStyledTextCtrl_GetHighlightGuide, 3094).
--define(wxStyledTextCtrl_GetLineEndPosition, 3095).
--define(wxStyledTextCtrl_GetCodePage, 3096).
--define(wxStyledTextCtrl_GetCaretForeground, 3097).
--define(wxStyledTextCtrl_GetReadOnly, 3098).
--define(wxStyledTextCtrl_SetCurrentPos, 3099).
--define(wxStyledTextCtrl_SetSelectionStart, 3100).
--define(wxStyledTextCtrl_GetSelectionStart, 3101).
--define(wxStyledTextCtrl_SetSelectionEnd, 3102).
--define(wxStyledTextCtrl_GetSelectionEnd, 3103).
--define(wxStyledTextCtrl_SetPrintMagnification, 3104).
--define(wxStyledTextCtrl_GetPrintMagnification, 3105).
--define(wxStyledTextCtrl_SetPrintColourMode, 3106).
--define(wxStyledTextCtrl_GetPrintColourMode, 3107).
--define(wxStyledTextCtrl_FindText, 3108).
--define(wxStyledTextCtrl_FormatRange, 3109).
--define(wxStyledTextCtrl_GetFirstVisibleLine, 3110).
--define(wxStyledTextCtrl_GetLine, 3111).
--define(wxStyledTextCtrl_GetLineCount, 3112).
--define(wxStyledTextCtrl_SetMarginLeft, 3113).
--define(wxStyledTextCtrl_GetMarginLeft, 3114).
--define(wxStyledTextCtrl_SetMarginRight, 3115).
--define(wxStyledTextCtrl_GetMarginRight, 3116).
--define(wxStyledTextCtrl_GetModify, 3117).
--define(wxStyledTextCtrl_SetSelection, 3118).
--define(wxStyledTextCtrl_GetSelectedText, 3119).
--define(wxStyledTextCtrl_GetTextRange, 3120).
--define(wxStyledTextCtrl_HideSelection, 3121).
--define(wxStyledTextCtrl_LineFromPosition, 3122).
--define(wxStyledTextCtrl_PositionFromLine, 3123).
--define(wxStyledTextCtrl_LineScroll, 3124).
--define(wxStyledTextCtrl_EnsureCaretVisible, 3125).
--define(wxStyledTextCtrl_ReplaceSelection, 3126).
--define(wxStyledTextCtrl_SetReadOnly, 3127).
--define(wxStyledTextCtrl_CanPaste, 3128).
--define(wxStyledTextCtrl_CanUndo, 3129).
--define(wxStyledTextCtrl_EmptyUndoBuffer, 3130).
--define(wxStyledTextCtrl_Undo, 3131).
--define(wxStyledTextCtrl_Cut, 3132).
--define(wxStyledTextCtrl_Copy, 3133).
--define(wxStyledTextCtrl_Paste, 3134).
--define(wxStyledTextCtrl_Clear, 3135).
--define(wxStyledTextCtrl_SetText, 3136).
--define(wxStyledTextCtrl_GetText, 3137).
--define(wxStyledTextCtrl_GetTextLength, 3138).
--define(wxStyledTextCtrl_GetOvertype, 3139).
--define(wxStyledTextCtrl_SetCaretWidth, 3140).
--define(wxStyledTextCtrl_GetCaretWidth, 3141).
--define(wxStyledTextCtrl_SetTargetStart, 3142).
--define(wxStyledTextCtrl_GetTargetStart, 3143).
--define(wxStyledTextCtrl_SetTargetEnd, 3144).
--define(wxStyledTextCtrl_GetTargetEnd, 3145).
--define(wxStyledTextCtrl_ReplaceTarget, 3146).
--define(wxStyledTextCtrl_SearchInTarget, 3147).
--define(wxStyledTextCtrl_SetSearchFlags, 3148).
--define(wxStyledTextCtrl_GetSearchFlags, 3149).
--define(wxStyledTextCtrl_CallTipShow, 3150).
--define(wxStyledTextCtrl_CallTipCancel, 3151).
--define(wxStyledTextCtrl_CallTipActive, 3152).
--define(wxStyledTextCtrl_CallTipPosAtStart, 3153).
--define(wxStyledTextCtrl_CallTipSetHighlight, 3154).
--define(wxStyledTextCtrl_CallTipSetBackground, 3155).
--define(wxStyledTextCtrl_CallTipSetForeground, 3156).
--define(wxStyledTextCtrl_CallTipSetForegroundHighlight, 3157).
--define(wxStyledTextCtrl_CallTipUseStyle, 3158).
--define(wxStyledTextCtrl_VisibleFromDocLine, 3159).
--define(wxStyledTextCtrl_DocLineFromVisible, 3160).
--define(wxStyledTextCtrl_WrapCount, 3161).
--define(wxStyledTextCtrl_SetFoldLevel, 3162).
--define(wxStyledTextCtrl_GetFoldLevel, 3163).
--define(wxStyledTextCtrl_GetLastChild, 3164).
--define(wxStyledTextCtrl_GetFoldParent, 3165).
--define(wxStyledTextCtrl_ShowLines, 3166).
--define(wxStyledTextCtrl_HideLines, 3167).
--define(wxStyledTextCtrl_GetLineVisible, 3168).
--define(wxStyledTextCtrl_SetFoldExpanded, 3169).
--define(wxStyledTextCtrl_GetFoldExpanded, 3170).
--define(wxStyledTextCtrl_ToggleFold, 3171).
--define(wxStyledTextCtrl_EnsureVisible, 3172).
--define(wxStyledTextCtrl_SetFoldFlags, 3173).
--define(wxStyledTextCtrl_EnsureVisibleEnforcePolicy, 3174).
--define(wxStyledTextCtrl_SetTabIndents, 3175).
--define(wxStyledTextCtrl_GetTabIndents, 3176).
--define(wxStyledTextCtrl_SetBackSpaceUnIndents, 3177).
--define(wxStyledTextCtrl_GetBackSpaceUnIndents, 3178).
--define(wxStyledTextCtrl_SetMouseDwellTime, 3179).
--define(wxStyledTextCtrl_GetMouseDwellTime, 3180).
--define(wxStyledTextCtrl_WordStartPosition, 3181).
--define(wxStyledTextCtrl_WordEndPosition, 3182).
--define(wxStyledTextCtrl_SetWrapMode, 3183).
--define(wxStyledTextCtrl_GetWrapMode, 3184).
--define(wxStyledTextCtrl_SetWrapVisualFlags, 3185).
--define(wxStyledTextCtrl_GetWrapVisualFlags, 3186).
--define(wxStyledTextCtrl_SetWrapVisualFlagsLocation, 3187).
--define(wxStyledTextCtrl_GetWrapVisualFlagsLocation, 3188).
--define(wxStyledTextCtrl_SetWrapStartIndent, 3189).
--define(wxStyledTextCtrl_GetWrapStartIndent, 3190).
--define(wxStyledTextCtrl_SetLayoutCache, 3191).
--define(wxStyledTextCtrl_GetLayoutCache, 3192).
--define(wxStyledTextCtrl_SetScrollWidth, 3193).
--define(wxStyledTextCtrl_GetScrollWidth, 3194).
--define(wxStyledTextCtrl_TextWidth, 3195).
--define(wxStyledTextCtrl_GetEndAtLastLine, 3196).
--define(wxStyledTextCtrl_TextHeight, 3197).
--define(wxStyledTextCtrl_SetUseVerticalScrollBar, 3198).
--define(wxStyledTextCtrl_GetUseVerticalScrollBar, 3199).
--define(wxStyledTextCtrl_AppendText, 3200).
--define(wxStyledTextCtrl_GetTwoPhaseDraw, 3201).
--define(wxStyledTextCtrl_SetTwoPhaseDraw, 3202).
--define(wxStyledTextCtrl_TargetFromSelection, 3203).
--define(wxStyledTextCtrl_LinesJoin, 3204).
--define(wxStyledTextCtrl_LinesSplit, 3205).
--define(wxStyledTextCtrl_SetFoldMarginColour, 3206).
--define(wxStyledTextCtrl_SetFoldMarginHiColour, 3207).
--define(wxStyledTextCtrl_LineDown, 3208).
--define(wxStyledTextCtrl_LineDownExtend, 3209).
--define(wxStyledTextCtrl_LineUp, 3210).
--define(wxStyledTextCtrl_LineUpExtend, 3211).
--define(wxStyledTextCtrl_CharLeft, 3212).
--define(wxStyledTextCtrl_CharLeftExtend, 3213).
--define(wxStyledTextCtrl_CharRight, 3214).
--define(wxStyledTextCtrl_CharRightExtend, 3215).
--define(wxStyledTextCtrl_WordLeft, 3216).
--define(wxStyledTextCtrl_WordLeftExtend, 3217).
--define(wxStyledTextCtrl_WordRight, 3218).
--define(wxStyledTextCtrl_WordRightExtend, 3219).
--define(wxStyledTextCtrl_Home, 3220).
--define(wxStyledTextCtrl_HomeExtend, 3221).
--define(wxStyledTextCtrl_LineEnd, 3222).
--define(wxStyledTextCtrl_LineEndExtend, 3223).
--define(wxStyledTextCtrl_DocumentStart, 3224).
--define(wxStyledTextCtrl_DocumentStartExtend, 3225).
--define(wxStyledTextCtrl_DocumentEnd, 3226).
--define(wxStyledTextCtrl_DocumentEndExtend, 3227).
--define(wxStyledTextCtrl_PageUp, 3228).
--define(wxStyledTextCtrl_PageUpExtend, 3229).
--define(wxStyledTextCtrl_PageDown, 3230).
--define(wxStyledTextCtrl_PageDownExtend, 3231).
--define(wxStyledTextCtrl_EditToggleOvertype, 3232).
--define(wxStyledTextCtrl_Cancel, 3233).
--define(wxStyledTextCtrl_DeleteBack, 3234).
--define(wxStyledTextCtrl_Tab, 3235).
--define(wxStyledTextCtrl_BackTab, 3236).
--define(wxStyledTextCtrl_NewLine, 3237).
--define(wxStyledTextCtrl_FormFeed, 3238).
--define(wxStyledTextCtrl_VCHome, 3239).
--define(wxStyledTextCtrl_VCHomeExtend, 3240).
--define(wxStyledTextCtrl_ZoomIn, 3241).
--define(wxStyledTextCtrl_ZoomOut, 3242).
--define(wxStyledTextCtrl_DelWordLeft, 3243).
--define(wxStyledTextCtrl_DelWordRight, 3244).
--define(wxStyledTextCtrl_LineCut, 3245).
--define(wxStyledTextCtrl_LineDelete, 3246).
--define(wxStyledTextCtrl_LineTranspose, 3247).
--define(wxStyledTextCtrl_LineDuplicate, 3248).
--define(wxStyledTextCtrl_LowerCase, 3249).
--define(wxStyledTextCtrl_UpperCase, 3250).
--define(wxStyledTextCtrl_LineScrollDown, 3251).
--define(wxStyledTextCtrl_LineScrollUp, 3252).
--define(wxStyledTextCtrl_DeleteBackNotLine, 3253).
--define(wxStyledTextCtrl_HomeDisplay, 3254).
--define(wxStyledTextCtrl_HomeDisplayExtend, 3255).
--define(wxStyledTextCtrl_LineEndDisplay, 3256).
--define(wxStyledTextCtrl_LineEndDisplayExtend, 3257).
--define(wxStyledTextCtrl_HomeWrapExtend, 3258).
--define(wxStyledTextCtrl_LineEndWrap, 3259).
--define(wxStyledTextCtrl_LineEndWrapExtend, 3260).
--define(wxStyledTextCtrl_VCHomeWrap, 3261).
--define(wxStyledTextCtrl_VCHomeWrapExtend, 3262).
--define(wxStyledTextCtrl_LineCopy, 3263).
--define(wxStyledTextCtrl_MoveCaretInsideView, 3264).
--define(wxStyledTextCtrl_LineLength, 3265).
--define(wxStyledTextCtrl_BraceHighlight, 3266).
--define(wxStyledTextCtrl_BraceBadLight, 3267).
--define(wxStyledTextCtrl_BraceMatch, 3268).
--define(wxStyledTextCtrl_GetViewEOL, 3269).
--define(wxStyledTextCtrl_SetViewEOL, 3270).
--define(wxStyledTextCtrl_SetModEventMask, 3271).
--define(wxStyledTextCtrl_GetEdgeColumn, 3272).
--define(wxStyledTextCtrl_SetEdgeColumn, 3273).
--define(wxStyledTextCtrl_SetEdgeMode, 3274).
--define(wxStyledTextCtrl_GetEdgeMode, 3275).
--define(wxStyledTextCtrl_GetEdgeColour, 3276).
--define(wxStyledTextCtrl_SetEdgeColour, 3277).
--define(wxStyledTextCtrl_SearchAnchor, 3278).
--define(wxStyledTextCtrl_SearchNext, 3279).
--define(wxStyledTextCtrl_SearchPrev, 3280).
--define(wxStyledTextCtrl_LinesOnScreen, 3281).
--define(wxStyledTextCtrl_UsePopUp, 3282).
--define(wxStyledTextCtrl_SelectionIsRectangle, 3283).
--define(wxStyledTextCtrl_SetZoom, 3284).
--define(wxStyledTextCtrl_GetZoom, 3285).
--define(wxStyledTextCtrl_GetModEventMask, 3286).
--define(wxStyledTextCtrl_SetSTCFocus, 3287).
--define(wxStyledTextCtrl_GetSTCFocus, 3288).
--define(wxStyledTextCtrl_SetStatus, 3289).
--define(wxStyledTextCtrl_GetStatus, 3290).
--define(wxStyledTextCtrl_SetMouseDownCaptures, 3291).
--define(wxStyledTextCtrl_GetMouseDownCaptures, 3292).
--define(wxStyledTextCtrl_SetSTCCursor, 3293).
--define(wxStyledTextCtrl_GetSTCCursor, 3294).
--define(wxStyledTextCtrl_SetControlCharSymbol, 3295).
--define(wxStyledTextCtrl_GetControlCharSymbol, 3296).
--define(wxStyledTextCtrl_WordPartLeft, 3297).
--define(wxStyledTextCtrl_WordPartLeftExtend, 3298).
--define(wxStyledTextCtrl_WordPartRight, 3299).
--define(wxStyledTextCtrl_WordPartRightExtend, 3300).
--define(wxStyledTextCtrl_SetVisiblePolicy, 3301).
--define(wxStyledTextCtrl_DelLineLeft, 3302).
--define(wxStyledTextCtrl_DelLineRight, 3303).
--define(wxStyledTextCtrl_GetXOffset, 3304).
--define(wxStyledTextCtrl_ChooseCaretX, 3305).
--define(wxStyledTextCtrl_SetXCaretPolicy, 3306).
--define(wxStyledTextCtrl_SetYCaretPolicy, 3307).
--define(wxStyledTextCtrl_GetPrintWrapMode, 3308).
--define(wxStyledTextCtrl_SetHotspotActiveForeground, 3309).
--define(wxStyledTextCtrl_SetHotspotActiveBackground, 3310).
--define(wxStyledTextCtrl_SetHotspotActiveUnderline, 3311).
--define(wxStyledTextCtrl_SetHotspotSingleLine, 3312).
--define(wxStyledTextCtrl_ParaDownExtend, 3313).
--define(wxStyledTextCtrl_ParaUp, 3314).
--define(wxStyledTextCtrl_ParaUpExtend, 3315).
--define(wxStyledTextCtrl_PositionBefore, 3316).
--define(wxStyledTextCtrl_PositionAfter, 3317).
--define(wxStyledTextCtrl_CopyRange, 3318).
--define(wxStyledTextCtrl_CopyText, 3319).
--define(wxStyledTextCtrl_SetSelectionMode, 3320).
--define(wxStyledTextCtrl_GetSelectionMode, 3321).
--define(wxStyledTextCtrl_LineDownRectExtend, 3322).
--define(wxStyledTextCtrl_LineUpRectExtend, 3323).
--define(wxStyledTextCtrl_CharLeftRectExtend, 3324).
--define(wxStyledTextCtrl_CharRightRectExtend, 3325).
--define(wxStyledTextCtrl_HomeRectExtend, 3326).
--define(wxStyledTextCtrl_VCHomeRectExtend, 3327).
--define(wxStyledTextCtrl_LineEndRectExtend, 3328).
--define(wxStyledTextCtrl_PageUpRectExtend, 3329).
--define(wxStyledTextCtrl_PageDownRectExtend, 3330).
--define(wxStyledTextCtrl_StutteredPageUp, 3331).
--define(wxStyledTextCtrl_StutteredPageUpExtend, 3332).
--define(wxStyledTextCtrl_StutteredPageDown, 3333).
--define(wxStyledTextCtrl_StutteredPageDownExtend, 3334).
--define(wxStyledTextCtrl_WordLeftEnd, 3335).
--define(wxStyledTextCtrl_WordLeftEndExtend, 3336).
--define(wxStyledTextCtrl_WordRightEnd, 3337).
--define(wxStyledTextCtrl_WordRightEndExtend, 3338).
--define(wxStyledTextCtrl_SetWhitespaceChars, 3339).
--define(wxStyledTextCtrl_SetCharsDefault, 3340).
--define(wxStyledTextCtrl_AutoCompGetCurrent, 3341).
--define(wxStyledTextCtrl_Allocate, 3342).
--define(wxStyledTextCtrl_FindColumn, 3343).
--define(wxStyledTextCtrl_GetCaretSticky, 3344).
--define(wxStyledTextCtrl_SetCaretSticky, 3345).
--define(wxStyledTextCtrl_ToggleCaretSticky, 3346).
--define(wxStyledTextCtrl_SetPasteConvertEndings, 3347).
--define(wxStyledTextCtrl_GetPasteConvertEndings, 3348).
--define(wxStyledTextCtrl_SelectionDuplicate, 3349).
--define(wxStyledTextCtrl_SetCaretLineBackAlpha, 3350).
--define(wxStyledTextCtrl_GetCaretLineBackAlpha, 3351).
--define(wxStyledTextCtrl_StartRecord, 3352).
--define(wxStyledTextCtrl_StopRecord, 3353).
--define(wxStyledTextCtrl_SetLexer, 3354).
--define(wxStyledTextCtrl_GetLexer, 3355).
--define(wxStyledTextCtrl_Colourise, 3356).
--define(wxStyledTextCtrl_SetProperty, 3357).
--define(wxStyledTextCtrl_SetKeyWords, 3358).
--define(wxStyledTextCtrl_SetLexerLanguage, 3359).
--define(wxStyledTextCtrl_GetProperty, 3360).
--define(wxStyledTextCtrl_GetStyleBitsNeeded, 3361).
--define(wxStyledTextCtrl_GetCurrentLine, 3362).
--define(wxStyledTextCtrl_StyleSetSpec, 3363).
--define(wxStyledTextCtrl_StyleSetFont, 3364).
--define(wxStyledTextCtrl_StyleSetFontAttr, 3365).
--define(wxStyledTextCtrl_StyleSetCharacterSet, 3366).
--define(wxStyledTextCtrl_StyleSetFontEncoding, 3367).
--define(wxStyledTextCtrl_CmdKeyExecute, 3368).
--define(wxStyledTextCtrl_SetMargins, 3369).
--define(wxStyledTextCtrl_GetSelection, 3370).
--define(wxStyledTextCtrl_PointFromPosition, 3371).
--define(wxStyledTextCtrl_ScrollToLine, 3372).
--define(wxStyledTextCtrl_ScrollToColumn, 3373).
--define(wxStyledTextCtrl_SetVScrollBar, 3374).
--define(wxStyledTextCtrl_SetHScrollBar, 3375).
--define(wxStyledTextCtrl_GetLastKeydownProcessed, 3376).
--define(wxStyledTextCtrl_SetLastKeydownProcessed, 3377).
--define(wxStyledTextCtrl_SaveFile, 3378).
--define(wxStyledTextCtrl_LoadFile, 3379).
--define(wxStyledTextCtrl_DoDragOver, 3380).
--define(wxStyledTextCtrl_DoDropText, 3381).
--define(wxStyledTextCtrl_GetUseAntiAliasing, 3382).
--define(wxStyledTextCtrl_AddTextRaw, 3383).
--define(wxStyledTextCtrl_InsertTextRaw, 3384).
--define(wxStyledTextCtrl_GetCurLineRaw, 3385).
--define(wxStyledTextCtrl_GetLineRaw, 3386).
--define(wxStyledTextCtrl_GetSelectedTextRaw, 3387).
--define(wxStyledTextCtrl_GetTextRangeRaw, 3388).
--define(wxStyledTextCtrl_SetTextRaw, 3389).
--define(wxStyledTextCtrl_GetTextRaw, 3390).
--define(wxStyledTextCtrl_AppendTextRaw, 3391).
--define(wxArtProvider_GetBitmap, 3392).
--define(wxArtProvider_GetIcon, 3393).
--define(wxTreeEvent_GetKeyCode, 3394).
--define(wxTreeEvent_GetItem, 3395).
--define(wxTreeEvent_GetKeyEvent, 3396).
--define(wxTreeEvent_GetLabel, 3397).
--define(wxTreeEvent_GetOldItem, 3398).
--define(wxTreeEvent_GetPoint, 3399).
--define(wxTreeEvent_IsEditCancelled, 3400).
--define(wxTreeEvent_SetToolTip, 3401).
--define(wxNotebookEvent_GetOldSelection, 3402).
--define(wxNotebookEvent_GetSelection, 3403).
--define(wxNotebookEvent_SetOldSelection, 3404).
--define(wxNotebookEvent_SetSelection, 3405).
--define(wxFileDataObject_new, 3406).
--define(wxFileDataObject_AddFile, 3407).
--define(wxFileDataObject_GetFilenames, 3408).
--define(wxFileDataObject_destroy, 3409).
--define(wxTextDataObject_new, 3410).
--define(wxTextDataObject_GetTextLength, 3411).
--define(wxTextDataObject_GetText, 3412).
--define(wxTextDataObject_SetText, 3413).
--define(wxTextDataObject_destroy, 3414).
--define(wxBitmapDataObject_new_1_1, 3415).
--define(wxBitmapDataObject_new_1_0, 3416).
--define(wxBitmapDataObject_GetBitmap, 3417).
--define(wxBitmapDataObject_SetBitmap, 3418).
--define(wxBitmapDataObject_destroy, 3419).
--define(wxClipboard_new, 3421).
--define(wxClipboard_destruct, 3422).
--define(wxClipboard_AddData, 3423).
--define(wxClipboard_Clear, 3424).
--define(wxClipboard_Close, 3425).
--define(wxClipboard_Flush, 3426).
--define(wxClipboard_GetData, 3427).
--define(wxClipboard_IsOpened, 3428).
--define(wxClipboard_Open, 3429).
--define(wxClipboard_SetData, 3430).
--define(wxClipboard_UsePrimarySelection, 3432).
--define(wxClipboard_IsSupported, 3433).
--define(wxClipboard_Get, 3434).
--define(wxSpinEvent_GetPosition, 3435).
--define(wxSpinEvent_SetPosition, 3436).
--define(wxSplitterWindow_new_0, 3437).
--define(wxSplitterWindow_new_2, 3438).
--define(wxSplitterWindow_destruct, 3439).
--define(wxSplitterWindow_Create, 3440).
--define(wxSplitterWindow_GetMinimumPaneSize, 3441).
--define(wxSplitterWindow_GetSashGravity, 3442).
--define(wxSplitterWindow_GetSashPosition, 3443).
--define(wxSplitterWindow_GetSplitMode, 3444).
--define(wxSplitterWindow_GetWindow1, 3445).
--define(wxSplitterWindow_GetWindow2, 3446).
--define(wxSplitterWindow_Initialize, 3447).
--define(wxSplitterWindow_IsSplit, 3448).
--define(wxSplitterWindow_ReplaceWindow, 3449).
--define(wxSplitterWindow_SetSashGravity, 3450).
--define(wxSplitterWindow_SetSashPosition, 3451).
--define(wxSplitterWindow_SetSashSize, 3452).
--define(wxSplitterWindow_SetMinimumPaneSize, 3453).
--define(wxSplitterWindow_SetSplitMode, 3454).
--define(wxSplitterWindow_SplitHorizontally, 3455).
--define(wxSplitterWindow_SplitVertically, 3456).
--define(wxSplitterWindow_Unsplit, 3457).
--define(wxSplitterWindow_UpdateSize, 3458).
--define(wxSplitterEvent_GetSashPosition, 3459).
--define(wxSplitterEvent_GetX, 3460).
--define(wxSplitterEvent_GetY, 3461).
--define(wxSplitterEvent_GetWindowBeingRemoved, 3462).
--define(wxSplitterEvent_SetSashPosition, 3463).
--define(wxHtmlWindow_new_0, 3464).
--define(wxHtmlWindow_new_2, 3465).
--define(wxHtmlWindow_AppendToPage, 3466).
--define(wxHtmlWindow_GetOpenedAnchor, 3467).
--define(wxHtmlWindow_GetOpenedPage, 3468).
--define(wxHtmlWindow_GetOpenedPageTitle, 3469).
--define(wxHtmlWindow_GetRelatedFrame, 3470).
--define(wxHtmlWindow_HistoryBack, 3471).
--define(wxHtmlWindow_HistoryCanBack, 3472).
--define(wxHtmlWindow_HistoryCanForward, 3473).
--define(wxHtmlWindow_HistoryClear, 3474).
--define(wxHtmlWindow_HistoryForward, 3475).
--define(wxHtmlWindow_LoadFile, 3476).
--define(wxHtmlWindow_LoadPage, 3477).
--define(wxHtmlWindow_SelectAll, 3478).
--define(wxHtmlWindow_SelectionToText, 3479).
--define(wxHtmlWindow_SelectLine, 3480).
--define(wxHtmlWindow_SelectWord, 3481).
--define(wxHtmlWindow_SetBorders, 3482).
--define(wxHtmlWindow_SetFonts, 3483).
--define(wxHtmlWindow_SetPage, 3484).
--define(wxHtmlWindow_SetRelatedFrame, 3485).
--define(wxHtmlWindow_SetRelatedStatusBar, 3486).
--define(wxHtmlWindow_ToText, 3487).
--define(wxHtmlWindow_destroy, 3488).
--define(wxHtmlLinkEvent_GetLinkInfo, 3489).
--define(wxSystemSettings_GetColour, 3490).
--define(wxSystemSettings_GetFont, 3491).
--define(wxSystemSettings_GetMetric, 3492).
--define(wxSystemSettings_GetScreenType, 3493).
--define(wxSystemOptions_GetOption, 3494).
--define(wxSystemOptions_GetOptionInt, 3495).
--define(wxSystemOptions_HasOption, 3496).
--define(wxSystemOptions_IsFalse, 3497).
--define(wxSystemOptions_SetOption_2_1, 3498).
--define(wxSystemOptions_SetOption_2_0, 3499).
--define(wxAuiNotebookEvent_SetSelection, 3500).
--define(wxAuiNotebookEvent_GetSelection, 3501).
--define(wxAuiNotebookEvent_SetOldSelection, 3502).
--define(wxAuiNotebookEvent_GetOldSelection, 3503).
--define(wxAuiNotebookEvent_SetDragSource, 3504).
--define(wxAuiNotebookEvent_GetDragSource, 3505).
--define(wxAuiManagerEvent_SetManager, 3506).
--define(wxAuiManagerEvent_GetManager, 3507).
--define(wxAuiManagerEvent_SetPane, 3508).
--define(wxAuiManagerEvent_GetPane, 3509).
--define(wxAuiManagerEvent_SetButton, 3510).
--define(wxAuiManagerEvent_GetButton, 3511).
--define(wxAuiManagerEvent_SetDC, 3512).
--define(wxAuiManagerEvent_GetDC, 3513).
--define(wxAuiManagerEvent_Veto, 3514).
--define(wxAuiManagerEvent_GetVeto, 3515).
--define(wxAuiManagerEvent_SetCanVeto, 3516).
--define(wxAuiManagerEvent_CanVeto, 3517).
--define(wxLogNull_new, 3518).
--define(wxLogNull_destroy, 3519).
--define(wxTaskBarIcon_new, 3520).
--define(wxTaskBarIcon_destruct, 3521).
--define(wxTaskBarIcon_PopupMenu, 3522).
--define(wxTaskBarIcon_RemoveIcon, 3523).
--define(wxTaskBarIcon_SetIcon, 3524).
+-define(gdicmn_wxDisplaySize, 2927).
+-define(gdicmn_wxSetCursor, 2928).
+-define(wxPrintout_new, 2929).
+-define(wxPrintout_destruct, 2930).
+-define(wxPrintout_GetDC, 2931).
+-define(wxPrintout_GetPageSizeMM, 2932).
+-define(wxPrintout_GetPageSizePixels, 2933).
+-define(wxPrintout_GetPaperRectPixels, 2934).
+-define(wxPrintout_GetPPIPrinter, 2935).
+-define(wxPrintout_GetPPIScreen, 2936).
+-define(wxPrintout_GetTitle, 2937).
+-define(wxPrintout_IsPreview, 2938).
+-define(wxPrintout_FitThisSizeToPaper, 2939).
+-define(wxPrintout_FitThisSizeToPage, 2940).
+-define(wxPrintout_FitThisSizeToPageMargins, 2941).
+-define(wxPrintout_MapScreenSizeToPaper, 2942).
+-define(wxPrintout_MapScreenSizeToPage, 2943).
+-define(wxPrintout_MapScreenSizeToPageMargins, 2944).
+-define(wxPrintout_MapScreenSizeToDevice, 2945).
+-define(wxPrintout_GetLogicalPaperRect, 2946).
+-define(wxPrintout_GetLogicalPageRect, 2947).
+-define(wxPrintout_GetLogicalPageMarginsRect, 2948).
+-define(wxPrintout_SetLogicalOrigin, 2949).
+-define(wxPrintout_OffsetLogicalOrigin, 2950).
+-define(wxStyledTextCtrl_new_2, 2951).
+-define(wxStyledTextCtrl_new_0, 2952).
+-define(wxStyledTextCtrl_destruct, 2953).
+-define(wxStyledTextCtrl_Create, 2954).
+-define(wxStyledTextCtrl_AddText, 2955).
+-define(wxStyledTextCtrl_AddStyledText, 2956).
+-define(wxStyledTextCtrl_InsertText, 2957).
+-define(wxStyledTextCtrl_ClearAll, 2958).
+-define(wxStyledTextCtrl_ClearDocumentStyle, 2959).
+-define(wxStyledTextCtrl_GetLength, 2960).
+-define(wxStyledTextCtrl_GetCharAt, 2961).
+-define(wxStyledTextCtrl_GetCurrentPos, 2962).
+-define(wxStyledTextCtrl_GetAnchor, 2963).
+-define(wxStyledTextCtrl_GetStyleAt, 2964).
+-define(wxStyledTextCtrl_Redo, 2965).
+-define(wxStyledTextCtrl_SetUndoCollection, 2966).
+-define(wxStyledTextCtrl_SelectAll, 2967).
+-define(wxStyledTextCtrl_SetSavePoint, 2968).
+-define(wxStyledTextCtrl_GetStyledText, 2969).
+-define(wxStyledTextCtrl_CanRedo, 2970).
+-define(wxStyledTextCtrl_MarkerLineFromHandle, 2971).
+-define(wxStyledTextCtrl_MarkerDeleteHandle, 2972).
+-define(wxStyledTextCtrl_GetUndoCollection, 2973).
+-define(wxStyledTextCtrl_GetViewWhiteSpace, 2974).
+-define(wxStyledTextCtrl_SetViewWhiteSpace, 2975).
+-define(wxStyledTextCtrl_PositionFromPoint, 2976).
+-define(wxStyledTextCtrl_PositionFromPointClose, 2977).
+-define(wxStyledTextCtrl_GotoLine, 2978).
+-define(wxStyledTextCtrl_GotoPos, 2979).
+-define(wxStyledTextCtrl_SetAnchor, 2980).
+-define(wxStyledTextCtrl_GetCurLine, 2981).
+-define(wxStyledTextCtrl_GetEndStyled, 2982).
+-define(wxStyledTextCtrl_ConvertEOLs, 2983).
+-define(wxStyledTextCtrl_GetEOLMode, 2984).
+-define(wxStyledTextCtrl_SetEOLMode, 2985).
+-define(wxStyledTextCtrl_StartStyling, 2986).
+-define(wxStyledTextCtrl_SetStyling, 2987).
+-define(wxStyledTextCtrl_GetBufferedDraw, 2988).
+-define(wxStyledTextCtrl_SetBufferedDraw, 2989).
+-define(wxStyledTextCtrl_SetTabWidth, 2990).
+-define(wxStyledTextCtrl_GetTabWidth, 2991).
+-define(wxStyledTextCtrl_SetCodePage, 2992).
+-define(wxStyledTextCtrl_MarkerDefine, 2993).
+-define(wxStyledTextCtrl_MarkerSetForeground, 2994).
+-define(wxStyledTextCtrl_MarkerSetBackground, 2995).
+-define(wxStyledTextCtrl_MarkerAdd, 2996).
+-define(wxStyledTextCtrl_MarkerDelete, 2997).
+-define(wxStyledTextCtrl_MarkerDeleteAll, 2998).
+-define(wxStyledTextCtrl_MarkerGet, 2999).
+-define(wxStyledTextCtrl_MarkerNext, 3000).
+-define(wxStyledTextCtrl_MarkerPrevious, 3001).
+-define(wxStyledTextCtrl_MarkerDefineBitmap, 3002).
+-define(wxStyledTextCtrl_MarkerAddSet, 3003).
+-define(wxStyledTextCtrl_MarkerSetAlpha, 3004).
+-define(wxStyledTextCtrl_SetMarginType, 3005).
+-define(wxStyledTextCtrl_GetMarginType, 3006).
+-define(wxStyledTextCtrl_SetMarginWidth, 3007).
+-define(wxStyledTextCtrl_GetMarginWidth, 3008).
+-define(wxStyledTextCtrl_SetMarginMask, 3009).
+-define(wxStyledTextCtrl_GetMarginMask, 3010).
+-define(wxStyledTextCtrl_SetMarginSensitive, 3011).
+-define(wxStyledTextCtrl_GetMarginSensitive, 3012).
+-define(wxStyledTextCtrl_StyleClearAll, 3013).
+-define(wxStyledTextCtrl_StyleSetForeground, 3014).
+-define(wxStyledTextCtrl_StyleSetBackground, 3015).
+-define(wxStyledTextCtrl_StyleSetBold, 3016).
+-define(wxStyledTextCtrl_StyleSetItalic, 3017).
+-define(wxStyledTextCtrl_StyleSetSize, 3018).
+-define(wxStyledTextCtrl_StyleSetFaceName, 3019).
+-define(wxStyledTextCtrl_StyleSetEOLFilled, 3020).
+-define(wxStyledTextCtrl_StyleResetDefault, 3021).
+-define(wxStyledTextCtrl_StyleSetUnderline, 3022).
+-define(wxStyledTextCtrl_StyleSetCase, 3023).
+-define(wxStyledTextCtrl_StyleSetHotSpot, 3024).
+-define(wxStyledTextCtrl_SetSelForeground, 3025).
+-define(wxStyledTextCtrl_SetSelBackground, 3026).
+-define(wxStyledTextCtrl_GetSelAlpha, 3027).
+-define(wxStyledTextCtrl_SetSelAlpha, 3028).
+-define(wxStyledTextCtrl_SetCaretForeground, 3029).
+-define(wxStyledTextCtrl_CmdKeyAssign, 3030).
+-define(wxStyledTextCtrl_CmdKeyClear, 3031).
+-define(wxStyledTextCtrl_CmdKeyClearAll, 3032).
+-define(wxStyledTextCtrl_SetStyleBytes, 3033).
+-define(wxStyledTextCtrl_StyleSetVisible, 3034).
+-define(wxStyledTextCtrl_GetCaretPeriod, 3035).
+-define(wxStyledTextCtrl_SetCaretPeriod, 3036).
+-define(wxStyledTextCtrl_SetWordChars, 3037).
+-define(wxStyledTextCtrl_BeginUndoAction, 3038).
+-define(wxStyledTextCtrl_EndUndoAction, 3039).
+-define(wxStyledTextCtrl_IndicatorSetStyle, 3040).
+-define(wxStyledTextCtrl_IndicatorGetStyle, 3041).
+-define(wxStyledTextCtrl_IndicatorSetForeground, 3042).
+-define(wxStyledTextCtrl_IndicatorGetForeground, 3043).
+-define(wxStyledTextCtrl_SetWhitespaceForeground, 3044).
+-define(wxStyledTextCtrl_SetWhitespaceBackground, 3045).
+-define(wxStyledTextCtrl_GetStyleBits, 3046).
+-define(wxStyledTextCtrl_SetLineState, 3047).
+-define(wxStyledTextCtrl_GetLineState, 3048).
+-define(wxStyledTextCtrl_GetMaxLineState, 3049).
+-define(wxStyledTextCtrl_GetCaretLineVisible, 3050).
+-define(wxStyledTextCtrl_SetCaretLineVisible, 3051).
+-define(wxStyledTextCtrl_GetCaretLineBackground, 3052).
+-define(wxStyledTextCtrl_SetCaretLineBackground, 3053).
+-define(wxStyledTextCtrl_AutoCompShow, 3054).
+-define(wxStyledTextCtrl_AutoCompCancel, 3055).
+-define(wxStyledTextCtrl_AutoCompActive, 3056).
+-define(wxStyledTextCtrl_AutoCompPosStart, 3057).
+-define(wxStyledTextCtrl_AutoCompComplete, 3058).
+-define(wxStyledTextCtrl_AutoCompStops, 3059).
+-define(wxStyledTextCtrl_AutoCompSetSeparator, 3060).
+-define(wxStyledTextCtrl_AutoCompGetSeparator, 3061).
+-define(wxStyledTextCtrl_AutoCompSelect, 3062).
+-define(wxStyledTextCtrl_AutoCompSetCancelAtStart, 3063).
+-define(wxStyledTextCtrl_AutoCompGetCancelAtStart, 3064).
+-define(wxStyledTextCtrl_AutoCompSetFillUps, 3065).
+-define(wxStyledTextCtrl_AutoCompSetChooseSingle, 3066).
+-define(wxStyledTextCtrl_AutoCompGetChooseSingle, 3067).
+-define(wxStyledTextCtrl_AutoCompSetIgnoreCase, 3068).
+-define(wxStyledTextCtrl_AutoCompGetIgnoreCase, 3069).
+-define(wxStyledTextCtrl_UserListShow, 3070).
+-define(wxStyledTextCtrl_AutoCompSetAutoHide, 3071).
+-define(wxStyledTextCtrl_AutoCompGetAutoHide, 3072).
+-define(wxStyledTextCtrl_AutoCompSetDropRestOfWord, 3073).
+-define(wxStyledTextCtrl_AutoCompGetDropRestOfWord, 3074).
+-define(wxStyledTextCtrl_RegisterImage, 3075).
+-define(wxStyledTextCtrl_ClearRegisteredImages, 3076).
+-define(wxStyledTextCtrl_AutoCompGetTypeSeparator, 3077).
+-define(wxStyledTextCtrl_AutoCompSetTypeSeparator, 3078).
+-define(wxStyledTextCtrl_AutoCompSetMaxWidth, 3079).
+-define(wxStyledTextCtrl_AutoCompGetMaxWidth, 3080).
+-define(wxStyledTextCtrl_AutoCompSetMaxHeight, 3081).
+-define(wxStyledTextCtrl_AutoCompGetMaxHeight, 3082).
+-define(wxStyledTextCtrl_SetIndent, 3083).
+-define(wxStyledTextCtrl_GetIndent, 3084).
+-define(wxStyledTextCtrl_SetUseTabs, 3085).
+-define(wxStyledTextCtrl_GetUseTabs, 3086).
+-define(wxStyledTextCtrl_SetLineIndentation, 3087).
+-define(wxStyledTextCtrl_GetLineIndentation, 3088).
+-define(wxStyledTextCtrl_GetLineIndentPosition, 3089).
+-define(wxStyledTextCtrl_GetColumn, 3090).
+-define(wxStyledTextCtrl_SetUseHorizontalScrollBar, 3091).
+-define(wxStyledTextCtrl_GetUseHorizontalScrollBar, 3092).
+-define(wxStyledTextCtrl_SetIndentationGuides, 3093).
+-define(wxStyledTextCtrl_GetIndentationGuides, 3094).
+-define(wxStyledTextCtrl_SetHighlightGuide, 3095).
+-define(wxStyledTextCtrl_GetHighlightGuide, 3096).
+-define(wxStyledTextCtrl_GetLineEndPosition, 3097).
+-define(wxStyledTextCtrl_GetCodePage, 3098).
+-define(wxStyledTextCtrl_GetCaretForeground, 3099).
+-define(wxStyledTextCtrl_GetReadOnly, 3100).
+-define(wxStyledTextCtrl_SetCurrentPos, 3101).
+-define(wxStyledTextCtrl_SetSelectionStart, 3102).
+-define(wxStyledTextCtrl_GetSelectionStart, 3103).
+-define(wxStyledTextCtrl_SetSelectionEnd, 3104).
+-define(wxStyledTextCtrl_GetSelectionEnd, 3105).
+-define(wxStyledTextCtrl_SetPrintMagnification, 3106).
+-define(wxStyledTextCtrl_GetPrintMagnification, 3107).
+-define(wxStyledTextCtrl_SetPrintColourMode, 3108).
+-define(wxStyledTextCtrl_GetPrintColourMode, 3109).
+-define(wxStyledTextCtrl_FindText, 3110).
+-define(wxStyledTextCtrl_FormatRange, 3111).
+-define(wxStyledTextCtrl_GetFirstVisibleLine, 3112).
+-define(wxStyledTextCtrl_GetLine, 3113).
+-define(wxStyledTextCtrl_GetLineCount, 3114).
+-define(wxStyledTextCtrl_SetMarginLeft, 3115).
+-define(wxStyledTextCtrl_GetMarginLeft, 3116).
+-define(wxStyledTextCtrl_SetMarginRight, 3117).
+-define(wxStyledTextCtrl_GetMarginRight, 3118).
+-define(wxStyledTextCtrl_GetModify, 3119).
+-define(wxStyledTextCtrl_SetSelection, 3120).
+-define(wxStyledTextCtrl_GetSelectedText, 3121).
+-define(wxStyledTextCtrl_GetTextRange, 3122).
+-define(wxStyledTextCtrl_HideSelection, 3123).
+-define(wxStyledTextCtrl_LineFromPosition, 3124).
+-define(wxStyledTextCtrl_PositionFromLine, 3125).
+-define(wxStyledTextCtrl_LineScroll, 3126).
+-define(wxStyledTextCtrl_EnsureCaretVisible, 3127).
+-define(wxStyledTextCtrl_ReplaceSelection, 3128).
+-define(wxStyledTextCtrl_SetReadOnly, 3129).
+-define(wxStyledTextCtrl_CanPaste, 3130).
+-define(wxStyledTextCtrl_CanUndo, 3131).
+-define(wxStyledTextCtrl_EmptyUndoBuffer, 3132).
+-define(wxStyledTextCtrl_Undo, 3133).
+-define(wxStyledTextCtrl_Cut, 3134).
+-define(wxStyledTextCtrl_Copy, 3135).
+-define(wxStyledTextCtrl_Paste, 3136).
+-define(wxStyledTextCtrl_Clear, 3137).
+-define(wxStyledTextCtrl_SetText, 3138).
+-define(wxStyledTextCtrl_GetText, 3139).
+-define(wxStyledTextCtrl_GetTextLength, 3140).
+-define(wxStyledTextCtrl_GetOvertype, 3141).
+-define(wxStyledTextCtrl_SetCaretWidth, 3142).
+-define(wxStyledTextCtrl_GetCaretWidth, 3143).
+-define(wxStyledTextCtrl_SetTargetStart, 3144).
+-define(wxStyledTextCtrl_GetTargetStart, 3145).
+-define(wxStyledTextCtrl_SetTargetEnd, 3146).
+-define(wxStyledTextCtrl_GetTargetEnd, 3147).
+-define(wxStyledTextCtrl_ReplaceTarget, 3148).
+-define(wxStyledTextCtrl_SearchInTarget, 3149).
+-define(wxStyledTextCtrl_SetSearchFlags, 3150).
+-define(wxStyledTextCtrl_GetSearchFlags, 3151).
+-define(wxStyledTextCtrl_CallTipShow, 3152).
+-define(wxStyledTextCtrl_CallTipCancel, 3153).
+-define(wxStyledTextCtrl_CallTipActive, 3154).
+-define(wxStyledTextCtrl_CallTipPosAtStart, 3155).
+-define(wxStyledTextCtrl_CallTipSetHighlight, 3156).
+-define(wxStyledTextCtrl_CallTipSetBackground, 3157).
+-define(wxStyledTextCtrl_CallTipSetForeground, 3158).
+-define(wxStyledTextCtrl_CallTipSetForegroundHighlight, 3159).
+-define(wxStyledTextCtrl_CallTipUseStyle, 3160).
+-define(wxStyledTextCtrl_VisibleFromDocLine, 3161).
+-define(wxStyledTextCtrl_DocLineFromVisible, 3162).
+-define(wxStyledTextCtrl_WrapCount, 3163).
+-define(wxStyledTextCtrl_SetFoldLevel, 3164).
+-define(wxStyledTextCtrl_GetFoldLevel, 3165).
+-define(wxStyledTextCtrl_GetLastChild, 3166).
+-define(wxStyledTextCtrl_GetFoldParent, 3167).
+-define(wxStyledTextCtrl_ShowLines, 3168).
+-define(wxStyledTextCtrl_HideLines, 3169).
+-define(wxStyledTextCtrl_GetLineVisible, 3170).
+-define(wxStyledTextCtrl_SetFoldExpanded, 3171).
+-define(wxStyledTextCtrl_GetFoldExpanded, 3172).
+-define(wxStyledTextCtrl_ToggleFold, 3173).
+-define(wxStyledTextCtrl_EnsureVisible, 3174).
+-define(wxStyledTextCtrl_SetFoldFlags, 3175).
+-define(wxStyledTextCtrl_EnsureVisibleEnforcePolicy, 3176).
+-define(wxStyledTextCtrl_SetTabIndents, 3177).
+-define(wxStyledTextCtrl_GetTabIndents, 3178).
+-define(wxStyledTextCtrl_SetBackSpaceUnIndents, 3179).
+-define(wxStyledTextCtrl_GetBackSpaceUnIndents, 3180).
+-define(wxStyledTextCtrl_SetMouseDwellTime, 3181).
+-define(wxStyledTextCtrl_GetMouseDwellTime, 3182).
+-define(wxStyledTextCtrl_WordStartPosition, 3183).
+-define(wxStyledTextCtrl_WordEndPosition, 3184).
+-define(wxStyledTextCtrl_SetWrapMode, 3185).
+-define(wxStyledTextCtrl_GetWrapMode, 3186).
+-define(wxStyledTextCtrl_SetWrapVisualFlags, 3187).
+-define(wxStyledTextCtrl_GetWrapVisualFlags, 3188).
+-define(wxStyledTextCtrl_SetWrapVisualFlagsLocation, 3189).
+-define(wxStyledTextCtrl_GetWrapVisualFlagsLocation, 3190).
+-define(wxStyledTextCtrl_SetWrapStartIndent, 3191).
+-define(wxStyledTextCtrl_GetWrapStartIndent, 3192).
+-define(wxStyledTextCtrl_SetLayoutCache, 3193).
+-define(wxStyledTextCtrl_GetLayoutCache, 3194).
+-define(wxStyledTextCtrl_SetScrollWidth, 3195).
+-define(wxStyledTextCtrl_GetScrollWidth, 3196).
+-define(wxStyledTextCtrl_TextWidth, 3197).
+-define(wxStyledTextCtrl_GetEndAtLastLine, 3198).
+-define(wxStyledTextCtrl_TextHeight, 3199).
+-define(wxStyledTextCtrl_SetUseVerticalScrollBar, 3200).
+-define(wxStyledTextCtrl_GetUseVerticalScrollBar, 3201).
+-define(wxStyledTextCtrl_AppendText, 3202).
+-define(wxStyledTextCtrl_GetTwoPhaseDraw, 3203).
+-define(wxStyledTextCtrl_SetTwoPhaseDraw, 3204).
+-define(wxStyledTextCtrl_TargetFromSelection, 3205).
+-define(wxStyledTextCtrl_LinesJoin, 3206).
+-define(wxStyledTextCtrl_LinesSplit, 3207).
+-define(wxStyledTextCtrl_SetFoldMarginColour, 3208).
+-define(wxStyledTextCtrl_SetFoldMarginHiColour, 3209).
+-define(wxStyledTextCtrl_LineDown, 3210).
+-define(wxStyledTextCtrl_LineDownExtend, 3211).
+-define(wxStyledTextCtrl_LineUp, 3212).
+-define(wxStyledTextCtrl_LineUpExtend, 3213).
+-define(wxStyledTextCtrl_CharLeft, 3214).
+-define(wxStyledTextCtrl_CharLeftExtend, 3215).
+-define(wxStyledTextCtrl_CharRight, 3216).
+-define(wxStyledTextCtrl_CharRightExtend, 3217).
+-define(wxStyledTextCtrl_WordLeft, 3218).
+-define(wxStyledTextCtrl_WordLeftExtend, 3219).
+-define(wxStyledTextCtrl_WordRight, 3220).
+-define(wxStyledTextCtrl_WordRightExtend, 3221).
+-define(wxStyledTextCtrl_Home, 3222).
+-define(wxStyledTextCtrl_HomeExtend, 3223).
+-define(wxStyledTextCtrl_LineEnd, 3224).
+-define(wxStyledTextCtrl_LineEndExtend, 3225).
+-define(wxStyledTextCtrl_DocumentStart, 3226).
+-define(wxStyledTextCtrl_DocumentStartExtend, 3227).
+-define(wxStyledTextCtrl_DocumentEnd, 3228).
+-define(wxStyledTextCtrl_DocumentEndExtend, 3229).
+-define(wxStyledTextCtrl_PageUp, 3230).
+-define(wxStyledTextCtrl_PageUpExtend, 3231).
+-define(wxStyledTextCtrl_PageDown, 3232).
+-define(wxStyledTextCtrl_PageDownExtend, 3233).
+-define(wxStyledTextCtrl_EditToggleOvertype, 3234).
+-define(wxStyledTextCtrl_Cancel, 3235).
+-define(wxStyledTextCtrl_DeleteBack, 3236).
+-define(wxStyledTextCtrl_Tab, 3237).
+-define(wxStyledTextCtrl_BackTab, 3238).
+-define(wxStyledTextCtrl_NewLine, 3239).
+-define(wxStyledTextCtrl_FormFeed, 3240).
+-define(wxStyledTextCtrl_VCHome, 3241).
+-define(wxStyledTextCtrl_VCHomeExtend, 3242).
+-define(wxStyledTextCtrl_ZoomIn, 3243).
+-define(wxStyledTextCtrl_ZoomOut, 3244).
+-define(wxStyledTextCtrl_DelWordLeft, 3245).
+-define(wxStyledTextCtrl_DelWordRight, 3246).
+-define(wxStyledTextCtrl_LineCut, 3247).
+-define(wxStyledTextCtrl_LineDelete, 3248).
+-define(wxStyledTextCtrl_LineTranspose, 3249).
+-define(wxStyledTextCtrl_LineDuplicate, 3250).
+-define(wxStyledTextCtrl_LowerCase, 3251).
+-define(wxStyledTextCtrl_UpperCase, 3252).
+-define(wxStyledTextCtrl_LineScrollDown, 3253).
+-define(wxStyledTextCtrl_LineScrollUp, 3254).
+-define(wxStyledTextCtrl_DeleteBackNotLine, 3255).
+-define(wxStyledTextCtrl_HomeDisplay, 3256).
+-define(wxStyledTextCtrl_HomeDisplayExtend, 3257).
+-define(wxStyledTextCtrl_LineEndDisplay, 3258).
+-define(wxStyledTextCtrl_LineEndDisplayExtend, 3259).
+-define(wxStyledTextCtrl_HomeWrapExtend, 3260).
+-define(wxStyledTextCtrl_LineEndWrap, 3261).
+-define(wxStyledTextCtrl_LineEndWrapExtend, 3262).
+-define(wxStyledTextCtrl_VCHomeWrap, 3263).
+-define(wxStyledTextCtrl_VCHomeWrapExtend, 3264).
+-define(wxStyledTextCtrl_LineCopy, 3265).
+-define(wxStyledTextCtrl_MoveCaretInsideView, 3266).
+-define(wxStyledTextCtrl_LineLength, 3267).
+-define(wxStyledTextCtrl_BraceHighlight, 3268).
+-define(wxStyledTextCtrl_BraceBadLight, 3269).
+-define(wxStyledTextCtrl_BraceMatch, 3270).
+-define(wxStyledTextCtrl_GetViewEOL, 3271).
+-define(wxStyledTextCtrl_SetViewEOL, 3272).
+-define(wxStyledTextCtrl_SetModEventMask, 3273).
+-define(wxStyledTextCtrl_GetEdgeColumn, 3274).
+-define(wxStyledTextCtrl_SetEdgeColumn, 3275).
+-define(wxStyledTextCtrl_SetEdgeMode, 3276).
+-define(wxStyledTextCtrl_GetEdgeMode, 3277).
+-define(wxStyledTextCtrl_GetEdgeColour, 3278).
+-define(wxStyledTextCtrl_SetEdgeColour, 3279).
+-define(wxStyledTextCtrl_SearchAnchor, 3280).
+-define(wxStyledTextCtrl_SearchNext, 3281).
+-define(wxStyledTextCtrl_SearchPrev, 3282).
+-define(wxStyledTextCtrl_LinesOnScreen, 3283).
+-define(wxStyledTextCtrl_UsePopUp, 3284).
+-define(wxStyledTextCtrl_SelectionIsRectangle, 3285).
+-define(wxStyledTextCtrl_SetZoom, 3286).
+-define(wxStyledTextCtrl_GetZoom, 3287).
+-define(wxStyledTextCtrl_GetModEventMask, 3288).
+-define(wxStyledTextCtrl_SetSTCFocus, 3289).
+-define(wxStyledTextCtrl_GetSTCFocus, 3290).
+-define(wxStyledTextCtrl_SetStatus, 3291).
+-define(wxStyledTextCtrl_GetStatus, 3292).
+-define(wxStyledTextCtrl_SetMouseDownCaptures, 3293).
+-define(wxStyledTextCtrl_GetMouseDownCaptures, 3294).
+-define(wxStyledTextCtrl_SetSTCCursor, 3295).
+-define(wxStyledTextCtrl_GetSTCCursor, 3296).
+-define(wxStyledTextCtrl_SetControlCharSymbol, 3297).
+-define(wxStyledTextCtrl_GetControlCharSymbol, 3298).
+-define(wxStyledTextCtrl_WordPartLeft, 3299).
+-define(wxStyledTextCtrl_WordPartLeftExtend, 3300).
+-define(wxStyledTextCtrl_WordPartRight, 3301).
+-define(wxStyledTextCtrl_WordPartRightExtend, 3302).
+-define(wxStyledTextCtrl_SetVisiblePolicy, 3303).
+-define(wxStyledTextCtrl_DelLineLeft, 3304).
+-define(wxStyledTextCtrl_DelLineRight, 3305).
+-define(wxStyledTextCtrl_GetXOffset, 3306).
+-define(wxStyledTextCtrl_ChooseCaretX, 3307).
+-define(wxStyledTextCtrl_SetXCaretPolicy, 3308).
+-define(wxStyledTextCtrl_SetYCaretPolicy, 3309).
+-define(wxStyledTextCtrl_GetPrintWrapMode, 3310).
+-define(wxStyledTextCtrl_SetHotspotActiveForeground, 3311).
+-define(wxStyledTextCtrl_SetHotspotActiveBackground, 3312).
+-define(wxStyledTextCtrl_SetHotspotActiveUnderline, 3313).
+-define(wxStyledTextCtrl_SetHotspotSingleLine, 3314).
+-define(wxStyledTextCtrl_ParaDownExtend, 3315).
+-define(wxStyledTextCtrl_ParaUp, 3316).
+-define(wxStyledTextCtrl_ParaUpExtend, 3317).
+-define(wxStyledTextCtrl_PositionBefore, 3318).
+-define(wxStyledTextCtrl_PositionAfter, 3319).
+-define(wxStyledTextCtrl_CopyRange, 3320).
+-define(wxStyledTextCtrl_CopyText, 3321).
+-define(wxStyledTextCtrl_SetSelectionMode, 3322).
+-define(wxStyledTextCtrl_GetSelectionMode, 3323).
+-define(wxStyledTextCtrl_LineDownRectExtend, 3324).
+-define(wxStyledTextCtrl_LineUpRectExtend, 3325).
+-define(wxStyledTextCtrl_CharLeftRectExtend, 3326).
+-define(wxStyledTextCtrl_CharRightRectExtend, 3327).
+-define(wxStyledTextCtrl_HomeRectExtend, 3328).
+-define(wxStyledTextCtrl_VCHomeRectExtend, 3329).
+-define(wxStyledTextCtrl_LineEndRectExtend, 3330).
+-define(wxStyledTextCtrl_PageUpRectExtend, 3331).
+-define(wxStyledTextCtrl_PageDownRectExtend, 3332).
+-define(wxStyledTextCtrl_StutteredPageUp, 3333).
+-define(wxStyledTextCtrl_StutteredPageUpExtend, 3334).
+-define(wxStyledTextCtrl_StutteredPageDown, 3335).
+-define(wxStyledTextCtrl_StutteredPageDownExtend, 3336).
+-define(wxStyledTextCtrl_WordLeftEnd, 3337).
+-define(wxStyledTextCtrl_WordLeftEndExtend, 3338).
+-define(wxStyledTextCtrl_WordRightEnd, 3339).
+-define(wxStyledTextCtrl_WordRightEndExtend, 3340).
+-define(wxStyledTextCtrl_SetWhitespaceChars, 3341).
+-define(wxStyledTextCtrl_SetCharsDefault, 3342).
+-define(wxStyledTextCtrl_AutoCompGetCurrent, 3343).
+-define(wxStyledTextCtrl_Allocate, 3344).
+-define(wxStyledTextCtrl_FindColumn, 3345).
+-define(wxStyledTextCtrl_GetCaretSticky, 3346).
+-define(wxStyledTextCtrl_SetCaretSticky, 3347).
+-define(wxStyledTextCtrl_ToggleCaretSticky, 3348).
+-define(wxStyledTextCtrl_SetPasteConvertEndings, 3349).
+-define(wxStyledTextCtrl_GetPasteConvertEndings, 3350).
+-define(wxStyledTextCtrl_SelectionDuplicate, 3351).
+-define(wxStyledTextCtrl_SetCaretLineBackAlpha, 3352).
+-define(wxStyledTextCtrl_GetCaretLineBackAlpha, 3353).
+-define(wxStyledTextCtrl_StartRecord, 3354).
+-define(wxStyledTextCtrl_StopRecord, 3355).
+-define(wxStyledTextCtrl_SetLexer, 3356).
+-define(wxStyledTextCtrl_GetLexer, 3357).
+-define(wxStyledTextCtrl_Colourise, 3358).
+-define(wxStyledTextCtrl_SetProperty, 3359).
+-define(wxStyledTextCtrl_SetKeyWords, 3360).
+-define(wxStyledTextCtrl_SetLexerLanguage, 3361).
+-define(wxStyledTextCtrl_GetProperty, 3362).
+-define(wxStyledTextCtrl_GetStyleBitsNeeded, 3363).
+-define(wxStyledTextCtrl_GetCurrentLine, 3364).
+-define(wxStyledTextCtrl_StyleSetSpec, 3365).
+-define(wxStyledTextCtrl_StyleSetFont, 3366).
+-define(wxStyledTextCtrl_StyleSetFontAttr, 3367).
+-define(wxStyledTextCtrl_StyleSetCharacterSet, 3368).
+-define(wxStyledTextCtrl_StyleSetFontEncoding, 3369).
+-define(wxStyledTextCtrl_CmdKeyExecute, 3370).
+-define(wxStyledTextCtrl_SetMargins, 3371).
+-define(wxStyledTextCtrl_GetSelection, 3372).
+-define(wxStyledTextCtrl_PointFromPosition, 3373).
+-define(wxStyledTextCtrl_ScrollToLine, 3374).
+-define(wxStyledTextCtrl_ScrollToColumn, 3375).
+-define(wxStyledTextCtrl_SetVScrollBar, 3376).
+-define(wxStyledTextCtrl_SetHScrollBar, 3377).
+-define(wxStyledTextCtrl_GetLastKeydownProcessed, 3378).
+-define(wxStyledTextCtrl_SetLastKeydownProcessed, 3379).
+-define(wxStyledTextCtrl_SaveFile, 3380).
+-define(wxStyledTextCtrl_LoadFile, 3381).
+-define(wxStyledTextCtrl_DoDragOver, 3382).
+-define(wxStyledTextCtrl_DoDropText, 3383).
+-define(wxStyledTextCtrl_GetUseAntiAliasing, 3384).
+-define(wxStyledTextCtrl_AddTextRaw, 3385).
+-define(wxStyledTextCtrl_InsertTextRaw, 3386).
+-define(wxStyledTextCtrl_GetCurLineRaw, 3387).
+-define(wxStyledTextCtrl_GetLineRaw, 3388).
+-define(wxStyledTextCtrl_GetSelectedTextRaw, 3389).
+-define(wxStyledTextCtrl_GetTextRangeRaw, 3390).
+-define(wxStyledTextCtrl_SetTextRaw, 3391).
+-define(wxStyledTextCtrl_GetTextRaw, 3392).
+-define(wxStyledTextCtrl_AppendTextRaw, 3393).
+-define(wxArtProvider_GetBitmap, 3394).
+-define(wxArtProvider_GetIcon, 3395).
+-define(wxTreeEvent_GetKeyCode, 3396).
+-define(wxTreeEvent_GetItem, 3397).
+-define(wxTreeEvent_GetKeyEvent, 3398).
+-define(wxTreeEvent_GetLabel, 3399).
+-define(wxTreeEvent_GetOldItem, 3400).
+-define(wxTreeEvent_GetPoint, 3401).
+-define(wxTreeEvent_IsEditCancelled, 3402).
+-define(wxTreeEvent_SetToolTip, 3403).
+-define(wxNotebookEvent_GetOldSelection, 3404).
+-define(wxNotebookEvent_GetSelection, 3405).
+-define(wxNotebookEvent_SetOldSelection, 3406).
+-define(wxNotebookEvent_SetSelection, 3407).
+-define(wxFileDataObject_new, 3408).
+-define(wxFileDataObject_AddFile, 3409).
+-define(wxFileDataObject_GetFilenames, 3410).
+-define(wxFileDataObject_destroy, 3411).
+-define(wxTextDataObject_new, 3412).
+-define(wxTextDataObject_GetTextLength, 3413).
+-define(wxTextDataObject_GetText, 3414).
+-define(wxTextDataObject_SetText, 3415).
+-define(wxTextDataObject_destroy, 3416).
+-define(wxBitmapDataObject_new_1_1, 3417).
+-define(wxBitmapDataObject_new_1_0, 3418).
+-define(wxBitmapDataObject_GetBitmap, 3419).
+-define(wxBitmapDataObject_SetBitmap, 3420).
+-define(wxBitmapDataObject_destroy, 3421).
+-define(wxClipboard_new, 3423).
+-define(wxClipboard_destruct, 3424).
+-define(wxClipboard_AddData, 3425).
+-define(wxClipboard_Clear, 3426).
+-define(wxClipboard_Close, 3427).
+-define(wxClipboard_Flush, 3428).
+-define(wxClipboard_GetData, 3429).
+-define(wxClipboard_IsOpened, 3430).
+-define(wxClipboard_Open, 3431).
+-define(wxClipboard_SetData, 3432).
+-define(wxClipboard_UsePrimarySelection, 3434).
+-define(wxClipboard_IsSupported, 3435).
+-define(wxClipboard_Get, 3436).
+-define(wxSpinEvent_GetPosition, 3437).
+-define(wxSpinEvent_SetPosition, 3438).
+-define(wxSplitterWindow_new_0, 3439).
+-define(wxSplitterWindow_new_2, 3440).
+-define(wxSplitterWindow_destruct, 3441).
+-define(wxSplitterWindow_Create, 3442).
+-define(wxSplitterWindow_GetMinimumPaneSize, 3443).
+-define(wxSplitterWindow_GetSashGravity, 3444).
+-define(wxSplitterWindow_GetSashPosition, 3445).
+-define(wxSplitterWindow_GetSplitMode, 3446).
+-define(wxSplitterWindow_GetWindow1, 3447).
+-define(wxSplitterWindow_GetWindow2, 3448).
+-define(wxSplitterWindow_Initialize, 3449).
+-define(wxSplitterWindow_IsSplit, 3450).
+-define(wxSplitterWindow_ReplaceWindow, 3451).
+-define(wxSplitterWindow_SetSashGravity, 3452).
+-define(wxSplitterWindow_SetSashPosition, 3453).
+-define(wxSplitterWindow_SetSashSize, 3454).
+-define(wxSplitterWindow_SetMinimumPaneSize, 3455).
+-define(wxSplitterWindow_SetSplitMode, 3456).
+-define(wxSplitterWindow_SplitHorizontally, 3457).
+-define(wxSplitterWindow_SplitVertically, 3458).
+-define(wxSplitterWindow_Unsplit, 3459).
+-define(wxSplitterWindow_UpdateSize, 3460).
+-define(wxSplitterEvent_GetSashPosition, 3461).
+-define(wxSplitterEvent_GetX, 3462).
+-define(wxSplitterEvent_GetY, 3463).
+-define(wxSplitterEvent_GetWindowBeingRemoved, 3464).
+-define(wxSplitterEvent_SetSashPosition, 3465).
+-define(wxHtmlWindow_new_0, 3466).
+-define(wxHtmlWindow_new_2, 3467).
+-define(wxHtmlWindow_AppendToPage, 3468).
+-define(wxHtmlWindow_GetOpenedAnchor, 3469).
+-define(wxHtmlWindow_GetOpenedPage, 3470).
+-define(wxHtmlWindow_GetOpenedPageTitle, 3471).
+-define(wxHtmlWindow_GetRelatedFrame, 3472).
+-define(wxHtmlWindow_HistoryBack, 3473).
+-define(wxHtmlWindow_HistoryCanBack, 3474).
+-define(wxHtmlWindow_HistoryCanForward, 3475).
+-define(wxHtmlWindow_HistoryClear, 3476).
+-define(wxHtmlWindow_HistoryForward, 3477).
+-define(wxHtmlWindow_LoadFile, 3478).
+-define(wxHtmlWindow_LoadPage, 3479).
+-define(wxHtmlWindow_SelectAll, 3480).
+-define(wxHtmlWindow_SelectionToText, 3481).
+-define(wxHtmlWindow_SelectLine, 3482).
+-define(wxHtmlWindow_SelectWord, 3483).
+-define(wxHtmlWindow_SetBorders, 3484).
+-define(wxHtmlWindow_SetFonts, 3485).
+-define(wxHtmlWindow_SetPage, 3486).
+-define(wxHtmlWindow_SetRelatedFrame, 3487).
+-define(wxHtmlWindow_SetRelatedStatusBar, 3488).
+-define(wxHtmlWindow_ToText, 3489).
+-define(wxHtmlWindow_destroy, 3490).
+-define(wxHtmlLinkEvent_GetLinkInfo, 3491).
+-define(wxSystemSettings_GetColour, 3492).
+-define(wxSystemSettings_GetFont, 3493).
+-define(wxSystemSettings_GetMetric, 3494).
+-define(wxSystemSettings_GetScreenType, 3495).
+-define(wxSystemOptions_GetOption, 3496).
+-define(wxSystemOptions_GetOptionInt, 3497).
+-define(wxSystemOptions_HasOption, 3498).
+-define(wxSystemOptions_IsFalse, 3499).
+-define(wxSystemOptions_SetOption_2_1, 3500).
+-define(wxSystemOptions_SetOption_2_0, 3501).
+-define(wxAuiNotebookEvent_SetSelection, 3502).
+-define(wxAuiNotebookEvent_GetSelection, 3503).
+-define(wxAuiNotebookEvent_SetOldSelection, 3504).
+-define(wxAuiNotebookEvent_GetOldSelection, 3505).
+-define(wxAuiNotebookEvent_SetDragSource, 3506).
+-define(wxAuiNotebookEvent_GetDragSource, 3507).
+-define(wxAuiManagerEvent_SetManager, 3508).
+-define(wxAuiManagerEvent_GetManager, 3509).
+-define(wxAuiManagerEvent_SetPane, 3510).
+-define(wxAuiManagerEvent_GetPane, 3511).
+-define(wxAuiManagerEvent_SetButton, 3512).
+-define(wxAuiManagerEvent_GetButton, 3513).
+-define(wxAuiManagerEvent_SetDC, 3514).
+-define(wxAuiManagerEvent_GetDC, 3515).
+-define(wxAuiManagerEvent_Veto, 3516).
+-define(wxAuiManagerEvent_GetVeto, 3517).
+-define(wxAuiManagerEvent_SetCanVeto, 3518).
+-define(wxAuiManagerEvent_CanVeto, 3519).
+-define(wxLogNull_new, 3520).
+-define(wxLogNull_destroy, 3521).
+-define(wxTaskBarIcon_new, 3522).
+-define(wxTaskBarIcon_destruct, 3523).
+-define(wxTaskBarIcon_PopupMenu, 3524).
+-define(wxTaskBarIcon_RemoveIcon, 3525).
+-define(wxTaskBarIcon_SetIcon, 3526).
+-define(wxLocale_new_0, 3527).
+-define(wxLocale_new_2, 3529).
+-define(wxLocale_destruct, 3530).
+-define(wxLocale_Init, 3532).
+-define(wxLocale_AddCatalog_1, 3533).
+-define(wxLocale_AddCatalog_3, 3534).
+-define(wxLocale_AddCatalogLookupPathPrefix, 3535).
+-define(wxLocale_GetCanonicalName, 3536).
+-define(wxLocale_GetLanguage, 3537).
+-define(wxLocale_GetLanguageName, 3538).
+-define(wxLocale_GetLocale, 3539).
+-define(wxLocale_GetName, 3540).
+-define(wxLocale_GetString_2, 3541).
+-define(wxLocale_GetString_4, 3542).
+-define(wxLocale_GetHeaderValue, 3543).
+-define(wxLocale_GetSysName, 3544).
+-define(wxLocale_GetSystemEncoding, 3545).
+-define(wxLocale_GetSystemEncodingName, 3546).
+-define(wxLocale_GetSystemLanguage, 3547).
+-define(wxLocale_IsLoaded, 3548).
+-define(wxLocale_IsOk, 3549).
diff --git a/lib/wx/src/wx.app.src b/lib/wx/src/wx.app.src
index e13982b0c1..d5ac478f20 100644
--- a/lib/wx/src/wx.app.src
+++ b/lib/wx/src/wx.app.src
@@ -33,5 +33,6 @@
]},
{registered, []},
{applications, [stdlib, kernel]},
- {env, []}
+ {env, []},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/wx/src/wxe_master.erl b/lib/wx/src/wxe_master.erl
index b98a7c793e..4b8a8f5720 100644
--- a/lib/wx/src/wxe_master.erl
+++ b/lib/wx/src/wxe_master.erl
@@ -28,7 +28,7 @@
-behaviour(gen_server).
%% API
--export([start/1, init_port/1, init_opengl/0]).
+-export([start/1, init_port/1, init_opengl/0, fetch_msgs/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -36,7 +36,9 @@
-record(state, {cb_port, %% Callback port and to erlang messages goes via it.
users, %% List of wx servers, needed ??
- driver}). %% Driver name so wx_server can create it's own port
+ driver, %% Driver name so wx_server can create it's own port
+ msgs=[] %% Early messages (such as openfiles on OSX)
+ }).
-include("wxe.hrl").
-include("gen/wxe_debug.hrl").
@@ -76,12 +78,18 @@ init_port(SilentStart) ->
%%--------------------------------------------------------------------
-%% Initlizes the opengl library
+%% Initalizes the opengl library
%%--------------------------------------------------------------------
init_opengl() ->
GLLib = wxe_util:wxgl_dl(),
wxe_util:call(?WXE_INIT_OPENGL, <<(list_to_binary(GLLib))/binary, 0:8>>).
+%%--------------------------------------------------------------------
+%% Fetch early messages, hack to get start up args on mac
+%%--------------------------------------------------------------------
+fetch_msgs() ->
+ gen_server:call(?MODULE, fetch_msgs, infinity).
+
%%====================================================================
%% gen_server callbacks
%%====================================================================
@@ -152,6 +160,8 @@ init([SilentStart]) ->
%%--------------------------------------------------------------------
handle_call(init_port, From, State=#state{driver=Driver,cb_port=CBPort, users=Users}) ->
{reply, {Driver,CBPort}, State#state{users=gb_sets:add(From,Users)}};
+handle_call(fetch_msgs, _From, State=#state{msgs=Msgs}) ->
+ {reply, lists:reverse(Msgs), State#state{msgs=[]}};
handle_call(_Request, _From, State) ->
%%io:format("Unknown request ~p sent to ~p from ~p ~n",[_Request, ?MODULE, _From]),
Reply = ok,
@@ -182,6 +192,8 @@ handle_info({wxe_driver, internal_error, Msg}, State) ->
handle_info({wxe_driver, debug, Msg}, State) ->
io:format("WX DBG: ~s~n", [Msg]),
{noreply, State};
+handle_info({wxe_driver, open_file, File}, State=#state{msgs=Msgs}) ->
+ {noreply, State#state{msgs=[File|Msgs]}};
handle_info(_Info, State) ->
io:format("Unknown message ~p sent to ~p~n",[_Info, ?MODULE]),
{noreply, State}.
diff --git a/lib/wx/src/wxe_server.erl b/lib/wx/src/wxe_server.erl
index aed9dca7ce..465b9da2e0 100644
--- a/lib/wx/src/wxe_server.erl
+++ b/lib/wx/src/wxe_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -192,17 +192,6 @@ handle_info({'DOWN',_,process,Pid,_}, State=#state{users=Users0,cleaners=Cs}) ->
{noreply, State}
end;
-handle_info(Msg = {'_wxe_destroy_', Pid}, State)
- when is_pid(Pid) ->
- case erlang:is_process_alive(Pid) of
- true ->
- Pid ! Msg,
- ok;
- false ->
- ok
- end,
- {noreply, State};
-
handle_info(_Info, State) ->
?log("Unknown message ~p sent to ~p~n",[_Info, ?MODULE]),
{noreply, State}.
diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl
index 7bdbd4594c..e3bbb21a23 100644
--- a/lib/wx/test/wx_basic_SUITE.erl
+++ b/lib/wx/test/wx_basic_SUITE.erl
@@ -341,23 +341,22 @@ wx_object(Config) ->
Me = self(),
?m({call, foobar, {Me, _}}, wx_object:call(Frame, foobar)),
?m(ok, wx_object:cast(Frame, foobar2)),
- ?m([{cast, foobar2}], flush()),
+ ?m([{cast, foobar2}|_], flush()),
FramePid = wx_object:get_pid(Frame),
io:format("wx_object pid ~p~n",[FramePid]),
FramePid ! foo3,
- ?m([{info, foo3}], flush()),
+ ?m([{info, foo3}|_], flush()),
?m(ok, wx_object:cast(Frame, fun(_) -> hehe end)),
- ?m([{cast, hehe}], flush()),
+ ?m([{cast, hehe}|_], flush()),
wxWindow:refresh(Frame),
- ?m([{sync_event, #wx{event=#wxPaint{}}, _}], flush()),
+ ?m([{sync_event, #wx{event=#wxPaint{}}, _}|_], flush()),
?m(ok, wx_object:cast(Frame, fun(_) -> timer:sleep(200), slept end)),
%% The sleep above should not hinder the Paint event below
%% Which it did in my buggy handling of the sync_callback
wxWindow:refresh(Frame),
- ?m([{sync_event, #wx{event=#wxPaint{}}, _}], flush()),
timer:sleep(500),
- ?m([{cast, slept}], flush()),
+ ?m([{sync_event, #wx{event=#wxPaint{}}, _}, {cast, slept}|_], flush()),
Monitor = erlang:monitor(process, FramePid),
case proplists:get_value(user, Config, false) of
@@ -397,7 +396,7 @@ check_events([], Async, Sync) ->
end.
flush() ->
- flush([], 500).
+ flush([], 1500).
flush(Acc, Wait) ->
receive
diff --git a/lib/wx/test/wx_class_SUITE.erl b/lib/wx/test/wx_class_SUITE.erl
index 0e151ccc9b..b375c9d515 100644
--- a/lib/wx/test/wx_class_SUITE.erl
+++ b/lib/wx/test/wx_class_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -344,21 +344,21 @@ listCtrlSort(Config) ->
Wx = wx:new(),
Frame = wxFrame:new(Wx, ?wxID_ANY, "Frame"),
- LC = wxListCtrl:new(Frame, [{style, ?wxLC_REPORT bor ?wxLC_SORT_ASCENDING}]),
+ LC = wxListCtrl:new(Frame, [{style, ?wxLC_REPORT}]),
%% must be done crashes in wxwidgets otherwise.
wxListCtrl:insertColumn(LC, 0, "Column"),
Add = fun(Int) ->
- wxListCtrl:insertItem(LC, Int, integer_to_list(Int)),
+ wxListCtrl:insertItem(LC, Int, "ABC " ++ integer_to_list(Int)),
%% ItemData Can only be integers currently
- wxListCtrl:setItemData(LC, Int, abs(2500-Int))
+ wxListCtrl:setItemData(LC, Int, abs(50-Int))
end,
- wx:foreach(Add, lists:seq(0,5000)),
+ wx:foreach(Add, lists:seq(0,50)),
wxWindow:show(Frame),
- timer:sleep(200),
+ timer:sleep(2000),
Sort = fun() ->
wxListCtrl:sortItems(LC, fun(A, B) ->
@@ -374,11 +374,12 @@ listCtrlSort(Config) ->
io:format("Sorted ~p ~n",[Time]),
Item = wxListItem:new(),
+ wxListItem:setMask(Item, ?wxLIST_MASK_TEXT),
_List = wx:map(fun(Int) ->
wxListItem:setId(Item, Int),
?m(true, wxListCtrl:getItem(LC, Item)),
- io:format("~s~n",[wxListItem:getText(Item)])
- end, lists:seq(0,100)),
+ io:format("~p: ~s~n",[Int, wxListItem:getText(Item)])
+ end, lists:seq(0,10)),
wxListItem:destroy(Item),
wx_test_lib:wx_destroy(Frame,Config).
diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk
index c018b4fb86..5523c20440 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1 +1 @@
-WX_VSN = 1.1.2
+WX_VSN = 1.2
diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml
index b020b9bfa3..3fa1f01a79 100644
--- a/lib/xmerl/doc/src/notes.xml
+++ b/lib/xmerl/doc/src/notes.xml
@@ -31,6 +31,35 @@
<p>This document describes the changes made to the Xmerl application.</p>
+<section><title>Xmerl 1.3.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Xmerl 1.3.6</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/xmerl/src/xmerl.app.src b/lib/xmerl/src/xmerl.app.src
index b471447bbd..45cfe9d250 100644
--- a/lib/xmerl/src/xmerl.app.src
+++ b/lib/xmerl/src/xmerl.app.src
@@ -39,5 +39,6 @@
{registered, []},
{env, []},
- {applications, [kernel, stdlib]}
+ {applications, [kernel, stdlib]},
+ {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]}
]}.
diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk
index 333466c11e..aab2a37d6c 100644
--- a/lib/xmerl/vsn.mk
+++ b/lib/xmerl/vsn.mk
@@ -1 +1 @@
-XMERL_VSN = 1.3.6
+XMERL_VSN = 1.3.7
diff --git a/make/ose_lm.mk.in b/make/ose_lm.mk.in
index 2e659ada1a..5455ad94c6 100644
--- a/make/ose_lm.mk.in
+++ b/make/ose_lm.mk.in
@@ -37,7 +37,9 @@ LDR1FLAGS = @erl_xcomp_ose_ldflags_pass1@
LDR2FLAGS = @erl_xcomp_ose_ldflags_pass2@
OSEROOT = @erl_xcomp_ose_OSEROOT@
LCF = @erl_xcomp_ose_LM_LCF@
-LMCONF = @erl_xcomp_ose_LM_CONF@
+BEAM_LMCONF = @erl_xcomp_ose_BEAM_LM_CONF@
+EPMD_LMCONF = @erl_xcomp_ose_EPMD_LM_CONF@
+RUN_ERL_LMCONF = @erl_xcomp_ose_RUN_ERL_LM_CONF@
STRIP = @erl_xcomp_ose_STRIP@
LM_POST_LINK = @erl_xcomp_ose_LM_POST_LINK@
LM_SET_CONF = @erl_xcomp_ose_LM_SET_CONF@
diff --git a/make/otp_released_app.mk b/make/otp_released_app.mk
new file mode 100644
index 0000000000..fb5205ab23
--- /dev/null
+++ b/make/otp_released_app.mk
@@ -0,0 +1,43 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2014. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+include $(APP_PWD)/vsn.mk
+include $(ERL_TOP)/make/otp_default_release_path.mk
+
+RELEASED_APP_VSN=$(APP)-$($(APP_VSN))
+ifeq ($(TESTROOT),)
+REL_DIR=$(OTP_DEFAULT_RELEASE_PATH)/releases/$(SYSTEM_VSN)
+else
+REL_DIR=$(TESTROOT)/releases/$(SYSTEM_VSN)
+endif
+INST_APP_VSNS=$(REL_DIR)/installed_application_versions
+
+.PHONY: update
+
+update:
+ test -d "$(REL_DIR)" || mkdir -p "$(REL_DIR)" ; \
+ if test ! -f "$(INST_APP_VSNS)" ; then \
+ echo "$(RELEASED_APP_VSN)" > "$(INST_APP_VSNS)" || exit 1; \
+ else \
+ if test x = x`grep $(RELEASED_APP_VSN) "$(INST_APP_VSNS)"` ; then \
+ echo $(RELEASED_APP_VSN) >> "$(INST_APP_VSNS)" || exit 1; \
+ fi ; \
+ fi
+
diff --git a/make/otp_subdir.mk b/make/otp_subdir.mk
index f31ab05c87..e6a75cce17 100644
--- a/make/otp_subdir.mk
+++ b/make/otp_subdir.mk
@@ -44,5 +44,14 @@ opt debug release docs release_docs tests release_tests clean depend valgrind st
fi ; \
done ; \
if test -f vsn.mk; then \
+ if test release = $@ && test ! -f SKIP; then \
+ app=`basename $$app_pwd` ; \
+ app_vsn=`echo $$app | sed "y|abcdefghijklmnopqrstuvwxyz|ABCDEFGHIJKLMNOPQRSTUVWXYZ|"` ; \
+ app_vsn=$${app_vsn}_VSN ; \
+ ( $(MAKE) -f "$(ERL_TOP)/make/otp_released_app.mk" \
+ APP_PWD="$$app_pwd" APP_VSN=$$app_vsn APP=$$app \
+ TESTROOT="$(TESTROOT)" update) \
+ || exit $$? ; \
+ fi ; \
echo "=== Leaving application" `basename $$app_pwd` ; \
fi
diff --git a/make/verify_runtime_dependencies b/make/verify_runtime_dependencies
new file mode 100755
index 0000000000..b8eea06b6e
--- /dev/null
+++ b/make/verify_runtime_dependencies
@@ -0,0 +1,313 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% @author Rickard Green <[email protected]>
+%%% @copyright (C) 2014, Rickard Green
+%%% @doc
+%%% Verify runtime dependencies when patching OTP applications.
+%%% @end
+%%% Created : 4 Mar 2014 by Rickard Green <[email protected]>
+%%%-------------------------------------------------------------------
+
+-mode(compile).
+
+-export([main/1]).
+
+main(Args) ->
+ {Force, Release, SourceDir, TargetDir, AppList} = parse_args(Args,
+ false,
+ [],
+ [],
+ [],
+ []),
+ SourceAppInfo = read_source_app_info(AppList, SourceDir),
+ AppVsnsTab0 = current_target_app_vsns(TargetDir, Release),
+ AppVsnsTab1 = add_source_app_vsns(SourceAppInfo, AppVsnsTab0),
+ case verify_runtime_deps(SourceAppInfo, AppVsnsTab1, true) of
+ true ->
+ ok;
+ false ->
+ case Force of
+ true ->
+ warn("Your OTP development system was updated with "
+ "unfulfilled runtime dependencies. The system "
+ "may not be working as expected.", []);
+ false ->
+ err("Unfulfilled runtime dependencies. "
+ "See warnings above.~n", [])
+ end
+ end,
+ halt(0).
+
+parse_args(["-force" | Args], _, Release, SourceDir, TargetDir, Apps) ->
+ parse_args(Args, true, Release, SourceDir, TargetDir, Apps);
+parse_args(["-release", Release | Args], Force, _, SourceDir, TargetDir, Apps) ->
+ parse_args(Args, Force, Release, SourceDir, TargetDir, Apps);
+parse_args(["-source", SourceDir | Args], Force, Release, _, TargetDir, Apps) ->
+ parse_args(Args, Force, Release, SourceDir, TargetDir, Apps);
+parse_args(["-target", TargetDir | Args], Force, Release, SourceDir, _, Apps) ->
+ parse_args(Args, Force, Release, SourceDir, TargetDir, Apps);
+parse_args([App | Args], Force, Release, SourceDir, TargetDir, OldApps) ->
+ parse_args(Args, Force, Release, SourceDir, TargetDir, [App | OldApps]);
+parse_args([], _, [], _, _, _) ->
+ err("Missing release~n", []);
+parse_args([], _, _, [], _, _) ->
+ err("Missing source directory~n", []);
+parse_args([], _, _, _, [], _) ->
+ err("Missing target directory~n", []);
+parse_args([], _, _, _, _, []) ->
+ err("Missing applications~n");
+parse_args([], Force, Release, SourceDir, TargetDir, Apps) ->
+ {Force, Release, SourceDir, TargetDir, Apps}.
+
+
+%warn(Format) ->
+% warn(Format, []).
+
+warn(Format, Args) ->
+ io:format(standard_error, "WARNING: " ++ Format, Args).
+
+err(Format) ->
+ err(Format, []).
+
+err(Format, Args) ->
+ io:format(standard_error, "ERROR: " ++ Format, Args),
+ halt(1).
+
+read_file(FileName) ->
+ case file:read_file(FileName) of
+ {ok, Content} ->
+ binary_to_list(Content);
+ {error, Error} ->
+ err("Failed to read ~s: ~p~n", [FileName, Error])
+ end.
+
+consult_file(FileName) ->
+ case file:consult(FileName) of
+ {ok, Terms} ->
+ Terms;
+ {error, Error} ->
+ err("Failed to consult ~s: ~p~n", [FileName, Error])
+ end.
+
+current_target_app_vsns(TargetDir, Release) ->
+ IAV = read_file(filename:join([TargetDir, "releases", Release,
+ "installed_application_versions"])),
+ DirList = string:tokens(IAV, "\n\r\t "),
+ LibDir = filename:join(TargetDir, "lib"),
+ make_app_vsns_tab(DirList, LibDir, gb_trees:empty()).
+
+make_app_vsns_tab([], _LibDir, GBT) ->
+ GBT;
+make_app_vsns_tab([AppVer | AppVsns], LibDir, GBT0) ->
+ GBT1 = try
+ case file:read_file_info(filename:join(LibDir, AppVer)) of
+ {ok, _FInfo} ->
+ [App, Vsn] = string:tokens(AppVer, "-"),
+ add_app_vsn(App, Vsn, GBT0);
+ _ ->
+ GBT0
+ end
+ catch
+ _:_ ->
+ warn("Unexpected directory: ~p~n",
+ [filename:join(LibDir, AppVer)]),
+ GBT0
+ end,
+ make_app_vsns_tab(AppVsns, LibDir, GBT1).
+
+add_app_vsn(App, VsnList, GBT) when is_atom(App) ->
+ Vsn = parse_vsn(VsnList),
+ case gb_trees:lookup(App, GBT) of
+ none ->
+ gb_trees:insert(App, [Vsn], GBT);
+ {value, Vsns} ->
+ gb_trees:update(App, [Vsn | Vsns], GBT)
+ end;
+add_app_vsn(AppStr, VsnList, GBT) ->
+ add_app_vsn(list_to_atom(AppStr), VsnList, GBT).
+
+add_source_app_vsns([], AppVsnsTab) ->
+ AppVsnsTab;
+add_source_app_vsns([{App, Vsn, _IReqs} | AI], AppVsnsTab) ->
+ add_source_app_vsns(AI, add_app_vsn(App, Vsn, AppVsnsTab)).
+
+read_source_app_info([], _SourceDir) ->
+ [];
+read_source_app_info([App | Apps], SourceDir) ->
+ AppFile = case App of
+ "erts" ->
+ filename:join([SourceDir, "erts", "preloaded", "ebin",
+ "erts.app"]);
+ _ ->
+ filename:join([SourceDir, "lib", App, "ebin",
+ App ++ ".app"])
+ end,
+ AppAtom = list_to_atom(App),
+ case consult_file(AppFile) of
+ [{application, AppAtom, InfoList}] ->
+ Vsn = case lists:keyfind(vsn, 1, InfoList) of
+ {vsn, V} ->
+ V;
+ _ ->
+ err("Missing vsn in ~p~n", AppFile)
+ end,
+ AI = case lists:keyfind(runtime_dependencies, 1, InfoList) of
+ {runtime_dependencies, IReqs} ->
+ case parse_inst_reqs(IReqs) of
+ error ->
+ err("Failed to parse runtime_dependencies in ~p~n",
+ [AppFile]);
+ ParsedIReqs ->
+ {AppAtom, Vsn, ParsedIReqs}
+ end;
+ _ ->
+ {AppAtom, Vsn, []}
+ end,
+ [AI | read_source_app_info(Apps, SourceDir)];
+ _ ->
+ err("Failed to parse ~p~n", [AppFile])
+ end.
+
+parse_vsn(VsnStr) ->
+ list_to_tuple(lists:map(fun (IL) ->
+ list_to_integer(IL)
+ end, string:tokens(VsnStr, "."))).
+
+parse_inst_reqs(InstReqs) ->
+ try
+ parse_inst_reqs_aux(InstReqs)
+ catch
+ _ : _ ->
+ error
+ end.
+
+parse_inst_reqs_aux([]) ->
+ [];
+parse_inst_reqs_aux([IR | IRs]) ->
+ [App, VsnStr] = string:tokens(IR, "-"),
+ [{list_to_atom(App), parse_vsn(VsnStr)} | parse_inst_reqs_aux(IRs)].
+
+make_app_vsn_str({App, VsnTup}) ->
+ make_app_vsn_str(tuple_to_list(VsnTup), [atom_to_list(App), $-]).
+
+make_app_vsn_str([I], Acc) ->
+ lists:flatten([Acc, integer_to_list(I)]);
+make_app_vsn_str([I | Is], Acc) ->
+ make_app_vsn_str(Is, [Acc, integer_to_list(I), $.]).
+
+missing_min_req(App, AppVsn, IReq) ->
+ warn("Unfulfilled runtime dependency for application ~p-~s: ~s~n",
+ [App, AppVsn, make_app_vsn_str(IReq)]).
+
+verify_runtime_deps([], _AppVsnsTab, Res) ->
+ Res;
+verify_runtime_deps([{App, Vsn, IReqs} | SAIs], AppVsnsTab, Res0) ->
+ Res = lists:foldl(
+ fun ({IRApp, IRMinVsn} = InstReq, AccRes) ->
+ case gb_trees:lookup(IRApp, AppVsnsTab) of
+ none ->
+ missing_min_req(App, Vsn, InstReq),
+ false;
+ {value, AppVsns} ->
+ try
+ lists:foreach(
+ fun (AppVsn) ->
+ case meets_min_req(AppVsn, IRMinVsn) of
+ true ->
+ throw(true);
+ false ->
+ false
+ end
+ end,
+ AppVsns),
+ missing_min_req(App, Vsn, InstReq),
+ false
+ catch
+ throw : true ->
+ AccRes
+ end
+ end
+ end,
+ Res0,
+ IReqs),
+ verify_runtime_deps(SAIs, AppVsnsTab, Res).
+
+meets_min_req(Vsn, Vsn) ->
+ true;
+meets_min_req({X}, VsnReq) ->
+ meets_min_req({X, 0, 0}, VsnReq);
+meets_min_req({X, Y}, VsnReq) ->
+ meets_min_req({X, Y, 0}, VsnReq);
+meets_min_req(Vsn, {X}) ->
+ meets_min_req(Vsn, {X, 0, 0});
+meets_min_req(Vsn, {X, Y}) ->
+ meets_min_req(Vsn, {X, Y, 0});
+meets_min_req({X, _Y, _Z}, {XReq, _YReq, _ZReq}) when X > XReq ->
+ true;
+meets_min_req({X, Y, _Z}, {X, YReq, _ZReq}) when Y > YReq ->
+ true;
+meets_min_req({X, Y, Z}, {X, Y, ZReq}) when Z > ZReq ->
+ true;
+meets_min_req({_X, _Y, _Z}, {_XReq, _YReq, _ZReq}) ->
+ false;
+meets_min_req(Vsn, VsnReq) ->
+ gp_meets_min_req(mk_gp_vsn_list(Vsn), mk_gp_vsn_list(VsnReq)).
+
+gp_meets_min_req([X, Y, Z | _Vs], [X, Y, Z]) ->
+ true;
+gp_meets_min_req([X, Y, Z | _Vs], [XReq, YReq, ZReq]) ->
+ meets_min_req({X, Y, Z}, {XReq, YReq, ZReq});
+gp_meets_min_req([X, Y, Z | Vs], [X, Y, Z | VReqs]) ->
+ gp_meets_min_req_tail(Vs, VReqs);
+gp_meets_min_req(_Vsn, _VReq) ->
+ %% Versions on different version branches, i.e., the minimum
+ %% required functionality is not included in Vsn.
+ false.
+
+gp_meets_min_req_tail([V | Vs], [V | VReqs]) ->
+ gp_meets_min_req_tail(Vs, VReqs);
+gp_meets_min_req_tail([], []) ->
+ true;
+gp_meets_min_req_tail([_V | _Vs], []) ->
+ true;
+gp_meets_min_req_tail([V | _Vs], [VReq]) when V > VReq ->
+ true;
+gp_meets_min_req_tail(_Vs, _VReqs) ->
+ %% Versions on different version branches, i.e., the minimum
+ %% required functionality is not included in Vsn.
+ false.
+
+mk_gp_vsn_list(Vsn) ->
+ [X, Y, Z | Tail] = tuple_to_list(Vsn),
+ [X, Y, Z | remove_trailing_zeroes(Tail)].
+
+remove_trailing_zeroes([]) ->
+ [];
+remove_trailing_zeroes([0 | Vs]) ->
+ case remove_trailing_zeroes(Vs) of
+ [] -> [];
+ NewVs -> [0 | NewVs]
+ end;
+remove_trailing_zeroes([V | Vs]) ->
+ [V | remove_trailing_zeroes(Vs)].
diff --git a/otp_build b/otp_build
index 6e016c88d2..fb001fc38f 100755
--- a/otp_build
+++ b/otp_build
@@ -52,9 +52,6 @@ usage ()
echo " release [-a] <target_dir> - creates full release to <target_dir>"
echo " smp [-a] - build an Erlang system, smp flavor only"
echo " tests <dir> - Build testsuites to <dir>"
- echo " patch_app <target_dir> <app1>... - build given apps to <target_dir>"
- echo " If core apps are patched, new start scripts will be created"
- echo " and 'Install' must be run again."
echo ""
echo "These are for cleaning up an open source distribution"
echo "with prebuilt files, so that it resembles the clean developers"
@@ -1192,129 +1189,6 @@ do_release ()
release || exit 1
}
-do_patch_app ()
-{
- # If target dir exists and has an installation of same major release, then
- # build given apps.
- # If patch includes erts, kernel, stdlib, sasl, then find latest
- # erts, kernel, stdlib, sasl and create .rel files.
- # Create .script/.boot
-
- if [ $# -lt 2 ]; then
- usage
- exit 1
- fi
-
- setup_make
- if [ X`$MAKE is_cross_configured` = Xyes ]; then
- TARGET=`$MAKE target_configured`
- fi
- target_dir=$1
- if [ ! -d $target_dir/releases/$otp_major_vsn ]; then
- echo "No OTP $otp_major_vsn installation in $target_dir" 1>&2
- exit 1
- fi
-
- shift
-
- otp_version=`cat "$target_dir/OTP_VERSION"` || { echo "Not able to read $target_dir/OTP_VERSION" 1>&2; exit 1; }
- { echo "$otp_version" | sed "s|^\([^\*]*\)\**|\1\*\*|g" > $target_dir/OTP_VERSION; } 2>/dev/null || { echo "Not able to update $target_dir/OTP_VERSION" 1>&2; exit 1; }
-
- # Build all applications to target
- for app in "$@"; do
- if [ "$app" = "erts" ] && [ -d $ERL_TOP/$app ]; then
- (cd $ERL_TOP/$app && $MAKE MAKE="$MAKE" TARGET=$TARGET \
- TESTROOT=$target_dir release) || exit 1
- elif [ "$app" != "erts" ] && [ -d $ERL_TOP/lib/$app ]; then
- (cd $ERL_TOP/lib/$app && $MAKE MAKE="$MAKE" TARGET=$TARGET \
- TESTROOT=$target_dir release) || exit 1
- else
- echo "Invalid application $app" 1>&2
- exit 1
- fi
- done
-
- # If erts, kernel, stdlib or sasl is included, find versions
- for app in "$@"; do
- if [ "$app" = "erts" ]; then
- erts_vsn=`grep '^VSN' erts/vsn.mk | sed "s|^VSN.*=[^0-9]*\([0-9].*\)$|\1|g"`
- update_rel=true
- elif [ "$app" = "kernel" ]; then
- kernel_vsn=`sed "s|^KERNEL_VSN[^=]*=[^0-9]*\([0-9].*\)$|\1|g" lib/kernel/vsn.mk`
- update_rel=true
- elif [ "$app" = "stdlib" ]; then
- stdlib_vsn=`sed "s|^STDLIB_VSN[^=]*=[^0-9]*\([0-9].*\)$|\1|g" lib/stdlib/vsn.mk`
- update_rel=true
- elif [ "$app" = "sasl" ]; then
- sasl_vsn=`sed "s|^SASL_VSN[^=]*=[^0-9]*\([0-9].*\)$|\1|g" lib/sasl/vsn.mk`
- update_rel=true
- fi
- done
-
- # and find the old versions for those not included
- if [ "X$update_rel" != "X" ]; then
- if [ "X$erts_vsn" = "X" ]; then
- erts_vsns=`ls -d $target_dir/erts-* | sed "s|$target_dir/erts-\([0-9\.].*\)|\1|g"`
- erts_vsn=`echo "$erts_vsns" | sort -t '.' -g | tail -n 1`
- fi
- if [ "X$kernel_vsn" = "X" ]; then
- kernel_vsns=`ls -d $target_dir/lib/kernel-* | sed "s|$target_dir/lib/kernel-\([0-9\.].*\)|\1|g"`
- kernel_vsn=`echo "$kernel_vsns" | sort -t '.' -g | tail -n 1`
- fi
- if [ "X$stdlib_vsn" = "X" ]; then
- stdlib_vsns=`ls -d $target_dir/lib/stdlib-* | sed "s|$target_dir/lib/stdlib-\([0-9\.].*\)|\1|g"`
- stdlib_vsn=`echo "$stdlib_vsns" | sort -t '.' -g | tail -n 1`
- fi
- if [ "X$sasl_vsn" = "X" ]; then
- sasl_vsns=`ls -d $target_dir/lib/sasl-* | sed "s|$target_dir/lib/sasl-\([0-9\.].*\)|\1|g"`
- sasl_vsn=`echo "$sasl_vsns" | sort -t '.' -g | tail -n 1`
- fi
-
- # Generate .rel, .script and .boot - to tmp dir
- start_clean="{release, {\"OTP APN 181 01\",\"$otp_major_vsn\"}, {erts, \"$erts_vsn\"},\n [{kernel,\"$kernel_vsn\"},\n {stdlib,\"$stdlib_vsn\"}]}.\n"
- start_sasl="{release, {\"OTP APN 181 01\",\"$otp_major_vsn\"}, {erts, \"$erts_vsn\"},\n [{kernel,\"$kernel_vsn\"},\n {stdlib,\"$stdlib_vsn\"},\n {sasl,\"$sasl_vsn\"}]}.\n"
-
- tmp_dir=$target_dir/tmp;
- if [ ! -d $tmp_dir ]; then
- mkdir $tmp_dir
- fi
- echo $start_sasl > $tmp_dir/start_sasl.rel
- echo $start_clean > $tmp_dir/start_clean.rel
- echo $start_clean > $tmp_dir/no_dot_erlang.rel
-
- erlc=$ERL_TOP/bootstrap/bin/erlc
- if [ ! -x $erlc ]; then
- echo "erlc not found, can not create .script and .boot files" 1>&2
- exit 1
- fi
-
- $erlc -I$target_dir/lib/*/ebin -o$tmp_dir $tmp_dir/start_sasl.rel || exit 1
- $erlc -I$target_dir/lib/*/ebin -o$tmp_dir +no_warn_sasl $tmp_dir/start_clean.rel || exit 1
- $erlc -I$target_dir/lib/*/ebin -o$tmp_dir +no_warn_sasl +no_dot_erlang $tmp_dir/no_dot_erlang.rel || exit 1
-
- # Generate RELEASES file
- erl=$ERL_TOP/bootstrap/bin/erl
- if [ ! -x $erl ]; then
- echo "erl not found, can not create RELEASES file" 1>&2
- exit 1
- fi
- $erl -noinput +B -eval "release_handler:create_RELEASES(\"%ERL_ROOT%\", \"$tmp_dir\", \"$tmp_dir/start_sasl.rel\", []), halt()" || exit 1
-
- # If all good so far, move generated files into target area
- mv $tmp_dir/RELEASES $target_dir/releases/RELEASES.src
- mv $tmp_dir/* $target_dir/releases/$otp_major_vsn
- rmdir $tmp_dir
-
- # Remove old start scripts (forces a new run of Install)
- rm -f $target_dir/releases/RELEASES
- rm -f $target_dir/bin/*.script
- rm -f $target_dir/bin/*.boot
- rm -f $target_dir/bin/erl
- fi
-
-}
-
-
do_tests ()
{
setup_make
@@ -1575,9 +1449,6 @@ case "$1" in
shift
fi;
do_release "$2";;
- patch_app)
- shift;
- do_patch_app "$@";;
tests)
if [ $minus_a_flag = true ]; then
shift
diff --git a/otp_versions.table b/otp_versions.table
new file mode 100644
index 0000000000..1120add531
--- /dev/null
+++ b/otp_versions.table
@@ -0,0 +1,3 @@
+OTP-17.0.2 : inets-5.10.1 ssh-3.0.2 # asn1-3.0 common_test-1.8 compiler-5.0 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.3 debugger-4.0 dialyzer-2.7 diameter-1.6 edoc-0.7.13 eldap-1.0.3 erl_docgen-0.3.5 erl_interface-3.7.16 erts-6.0.1 et-1.5 eunit-2.2.7 gs-1.5.16 hipe-3.10.3 ic-4.3.5 jinterface-1.5.9 kernel-3.0 megaco-3.17.1 mnesia-4.12 observer-2.0 odbc-2.10.20 orber-3.6.27 os_mon-2.2.15 ose-1.0 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22 reltool-0.6.5 runtime_tools-1.8.14 sasl-2.4 snmp-4.25.1 ssl-5.3.4 stdlib-2.0 syntax_tools-1.6.14 test_server-3.7 tools-2.6.14 typer-0.9.7 webtool-0.8.10 wx-1.2 xmerl-1.3.7 :
+OTP-17.0.1 : erts-6.0.1 typer-0.9.7 # asn1-3.0 common_test-1.8 compiler-5.0 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.3 debugger-4.0 dialyzer-2.7 diameter-1.6 edoc-0.7.13 eldap-1.0.3 erl_docgen-0.3.5 erl_interface-3.7.16 et-1.5 eunit-2.2.7 gs-1.5.16 hipe-3.10.3 ic-4.3.5 inets-5.10 jinterface-1.5.9 kernel-3.0 megaco-3.17.1 mnesia-4.12 observer-2.0 odbc-2.10.20 orber-3.6.27 os_mon-2.2.15 ose-1.0 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22 reltool-0.6.5 runtime_tools-1.8.14 sasl-2.4 snmp-4.25.1 ssh-3.0.1 ssl-5.3.4 stdlib-2.0 syntax_tools-1.6.14 test_server-3.7 tools-2.6.14 webtool-0.8.10 wx-1.2 xmerl-1.3.7 :
+OTP-17.0 : asn1-3.0 common_test-1.8 compiler-5.0 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.3 debugger-4.0 dialyzer-2.7 diameter-1.6 edoc-0.7.13 eldap-1.0.3 erl_docgen-0.3.5 erl_interface-3.7.16 erts-6.0 et-1.5 eunit-2.2.7 gs-1.5.16 hipe-3.10.3 ic-4.3.5 inets-5.10 jinterface-1.5.9 kernel-3.0 megaco-3.17.1 mnesia-4.12 observer-2.0 odbc-2.10.20 orber-3.6.27 os_mon-2.2.15 ose-1.0 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22 reltool-0.6.5 runtime_tools-1.8.14 sasl-2.4 snmp-4.25.1 ssh-3.0.1 ssl-5.3.4 stdlib-2.0 syntax_tools-1.6.14 test_server-3.7 tools-2.6.14 typer-0.9.6 webtool-0.8.10 wx-1.2 xmerl-1.3.7 # :
diff --git a/system/doc/design_principles/applications.xml b/system/doc/design_principles/applications.xml
index 228ca1f2bf..7b030115df 100644
--- a/system/doc/design_principles/applications.xml
+++ b/system/doc/design_principles/applications.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -157,8 +157,9 @@ ch_app:stop([])</code>
all applications have dependencies to at least <c>kernel</c>
and <c>stdlib</c>.</item>
</taglist>
- <p>The syntax and contents of of the application resource file
- are described in detail in <c>app(4)</c>.</p>
+ <note><p>The syntax and contents of of the application resource file
+ are described in detail in the<seealso marker="kernel:app">
+ Application resource file reference</seealso>.</p></note>
</section>
<section>
diff --git a/system/doc/design_principles/release_handling.xml b/system/doc/design_principles/release_handling.xml
index 2a5831b89f..ba8a88d1c2 100644
--- a/system/doc/design_principles/release_handling.xml
+++ b/system/doc/design_principles/release_handling.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2013</year>
+ <year>2003</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -329,13 +329,26 @@
automatically ensured.</p>
<p>When the release handler encounters the instruction, it first
generates a temporary boot file, which starts the new versions
- of the emulator and the core applications. Then it shuts down
+ of the emulator and the core applications, and the old version
+ of all other applications. Then it shuts down
the current emulator by calling <c>init:reboot()</c>, see
<c>init(3)</c>. All processes are terminated gracefully and
the system is rebooted by the heart program, using the
temporary boot file. After the reboot, the rest of the relup
instructions are executed. This is done as a part of the
temporary boot script.</p>
+ <warning>
+ <p>Since this mechanism causes the new versions of the
+ emulator and core applications to run with the old version of
+ other applications during startup, extra care must be taken to
+ avoid incompatibility. Incompatible changes in the core
+ applications may in some situations be necessary. If possible,
+ such changes are preceded by deprecation over two major
+ releases before the actual change. To make sure your
+ application is not crashed by an incompatible change, always
+ remove any call to deprecated functions as soon as
+ possible.</p>
+ </warning>
<p>An info report is written when the upgrade is completed. To
programatically find out if the upgrade is complete,
call <c>release_handler:which_releases(current)</c> and check
diff --git a/system/doc/design_principles/spec_proc.xml b/system/doc/design_principles/spec_proc.xml
index 8de7a5fe03..e4fb5fdca7 100644
--- a/system/doc/design_principles/spec_proc.xml
+++ b/system/doc/design_principles/spec_proc.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -130,7 +130,8 @@ ok
-export([alloc/0, free/1]).
-export([init/1]).
-export([system_continue/3, system_terminate/4,
- write_debug/3]).
+ write_debug/3,
+ system_get_state/1, system_replace_state/2]).
start_link() ->
proc_lib:start_link(ch4, init, [self()]).
@@ -156,15 +157,15 @@ init(Parent) ->
loop(Chs, Parent, Deb) ->
receive
{From, alloc} ->
- Deb2 = sys:handle_debug(Deb, {ch4, write_debug},
+ Deb2 = sys:handle_debug(Deb, fun ch4:write_debug/3,
ch4, {in, alloc, From}),
{Ch, Chs2} = alloc(Chs),
From ! {ch4, Ch},
- Deb3 = sys:handle_debug(Deb2, {ch4, write_debug},
+ Deb3 = sys:handle_debug(Deb2, fun ch4:write_debug/3,
ch4, {out, {ch4, Ch}, From}),
loop(Chs2, Parent, Deb3);
{free, Ch} ->
- Deb2 = sys:handle_debug(Deb, {ch4, write_debug},
+ Deb2 = sys:handle_debug(Deb, fun ch4:write_debug/3,
ch4, {in, {free, Ch}}),
Chs2 = free(Ch, Chs),
loop(Chs2, Parent, Deb2);
@@ -177,9 +178,16 @@ loop(Chs, Parent, Deb) ->
system_continue(Parent, Deb, Chs) ->
loop(Chs, Parent, Deb).
-system_terminate(Reason, Parent, Deb, Chs) ->
+system_terminate(Reason, _Parent, _Deb, _Chs) ->
exit(Reason).
+system_get_state(Chs) ->
+ {ok, Chs}.
+
+system_replace_state(StateFun, Chs) ->
+ NChs = StateFun(Chs),
+ {ok, NChs, NChs}.
+
write_debug(Dev, Event, Name) ->
io:format(Dev, "~p event = ~p~n", [Name, Event]).</pre>
<p>Example on how the simple debugging functions in <c>sys</c> can
@@ -281,10 +289,10 @@ sys:handle_debug(Deb, Func, Info, Event) => Deb1</code>
<p><c>Deb</c> is the debug structure.</p>
</item>
<item>
- <p><c>Func</c> is a tuple <c>{Module, Name}</c> (or a fun) and
- should specify a (user defined) function used to format
+ <p><c>Func</c> is a fun specifying
+ a (user defined) function used to format
trace output. For each system event, the format function is
- called as <c>Module:Name(Dev, Event, Info)</c>, where:</p>
+ called as <c>Func(Dev, Event, Info)</c>, where:</p>
<list type="bulleted">
<item>
<p><c>Dev</c> is the IO device to which the output should
@@ -319,15 +327,15 @@ sys:handle_debug(Deb, Func, Info, Event) => Deb1</code>
loop(Chs, Parent, Deb) ->
receive
{From, alloc} ->
- Deb2 = sys:handle_debug(Deb, {ch4, write_debug},
+ Deb2 = sys:handle_debug(Deb, fun ch4:write_debug/3,
ch4, {in, alloc, From}),
{Ch, Chs2} = alloc(Chs),
From ! {ch4, Ch},
- Deb3 = sys:handle_debug(Deb2, {ch4, write_debug},
+ Deb3 = sys:handle_debug(Deb2, fun ch4:write_debug/3,
ch4, {out, {ch4, Ch}, From}),
loop(Chs2, Parent, Deb3);
{free, Ch} ->
- Deb2 = sys:handle_debug(Deb, {ch4, write_debug},
+ Deb2 = sys:handle_debug(Deb, fun ch4:write_debug/3,
ch4, {in, {free, Ch}}),
Chs2 = free(Ch, Chs),
loop(Chs2, Parent, Deb2);
@@ -366,8 +374,15 @@ Module:system_terminate(Reason, Parent, Deb, State)</code>
<item><c>Module</c> is the name of the module.</item>
<item><c>Deb</c> is the debug structure.</item>
<item><c>State</c> is a term describing the internal state and
- is passed to <c>system_continue</c>/<c>system_terminate</c>.</item>
+ is passed to <c>system_continue</c>/<c>system_terminate</c>/
+ <c>system_get_state</c>/<c>system_replace_state</c>.</item>
</list>
+ <p>If the process should return its state <c>handle_system_msg</c> will call:</p>
+ <code type="none">
+Module:system_get_state(State)</code>
+ <p>or if the process should replace its state using the fun <c>StateFun</c>:</p>
+ <code type="none">
+Module:system_replace_state(StateFun, State)</code>
<p>In the example:</p>
<code type="none">
loop(Chs, Parent, Deb) ->
@@ -383,7 +398,15 @@ system_continue(Parent, Deb, Chs) ->
loop(Chs, Parent, Deb).
system_terminate(Reason, Parent, Deb, Chs) ->
- exit(Reason).</code>
+ exit(Reason).
+
+system_get_state(Chs) ->
+ {ok, Chs, Chs}.
+
+system_replace_state(StateFun, Chs) ->
+ NChs = StateFun(Chs),
+ {ok, NChs, NChs}.
+</code>
<p>If the special process is set to trap exits, note that if
the parent process terminates, the expected behavior is to
terminate with the same reason:</p>
@@ -408,11 +431,11 @@ loop(...) ->
<section>
<title>User-Defined Behaviours</title>
- <p>To implement a user-defined behaviour, write code similar to
+ <p><marker id="behaviours"/>To implement a user-defined behaviour, write code similar to
code for a special process but calling functions in a callback
module for handling specific tasks.</p>
<p>If it is desired that the compiler should warn for missing callback
- functions, as it does for the OTP behaviours, add callback attributes in the
+ functions, as it does for the OTP behaviours, add <c>-callback</c> attributes in the
behaviour module to describe the expected callbacks:</p>
<code type="none">
-callback Name1(Arg1_1, Arg1_2, ..., Arg1_N1) -> Res1.
@@ -422,15 +445,15 @@ loop(...) ->
<p>where <c>NameX</c> are the names of the expected callbacks and
<c>ArgX_Y</c>, <c>ResX</c> are types as they are described in Specifications
for functions in <seealso marker="../reference_manual/typespec">Types and
- Function Specifications</seealso>. The whole syntax of spec attributes is
- supported by callback attributes.</p>
+ Function Specifications</seealso>. The whole syntax of <c>-spec</c> attribute is
+ supported by <c>-callback</c> attribute.</p>
<p>Alternatively you may directly implement and export the function:</p>
<code type="none">
behaviour_info(callbacks) ->
- [{Name1,Arity1},...,{NameN,ArityN}].</code>
- <p>where each <c>{Name,Arity}</c> specifies the name and arity of a callback
+ [{Name1, Arity1},...,{NameN, ArityN}].</code>
+ <p>where each <c>{Name, Arity}</c> specifies the name and arity of a callback
function. This function is otherwise automatically generated by the compiler
- using the callback attributes.</p>
+ using the <c>-callback</c> attributes.</p>
<p>When the compiler encounters the module attribute
<c>-behaviour(Behaviour).</c> in a module <c>Mod</c>, it will call
<c>Behaviour:behaviour_info(callbacks)</c> and compare the result with the
diff --git a/system/doc/getting_started/seq_prog.xml b/system/doc/getting_started/seq_prog.xml
index 3830a34e5a..fd49102263 100644
--- a/system/doc/getting_started/seq_prog.xml
+++ b/system/doc/getting_started/seq_prog.xml
@@ -419,6 +419,129 @@ list_length([First | Rest]) ->
</section>
<section>
+ <title>Maps</title>
+ <p>Maps are a set of key to value associations. These associations
+ are encapsulated with "#{" and "}". To create an association from
+ "key" to value 42, we write:</p>
+<code type="none">
+> #{ "key" => 42 }.
+#{"key" => 42}</code>
+ <p>We will jump straight into the deep end with an example using some
+ interesting features.</p>
+ <p>The following example shows how we calculate alpha blending using
+ maps to reference color and alpha channels:</p>
+ <code type="none">
+-module(color).
+
+-export([new/4, blend/2]).
+
+-define(is_channel(V), (is_float(V) andalso V &gt;= 0.0 andalso V =&lt; 1.0)).
+
+new(R,G,B,A) when ?is_channel(R), ?is_channel(G),
+ ?is_channel(B), ?is_channel(A) ->
+ #{red =&gt; R, green =&gt; G, blue =&gt; B, alpha =&gt; A}.
+
+blend(Src,Dst) ->
+ blend(Src,Dst,alpha(Src,Dst)).
+
+blend(Src,Dst,Alpha) when Alpha > 0.0 ->
+ Dst#{
+ red := red(Src,Dst) / Alpha,
+ green := green(Src,Dst) / Alpha,
+ blue := blue(Src,Dst) / Alpha,
+ alpha := Alpha
+ };
+blend(_,Dst,_) ->
+ Dst#{
+ red := 0.0,
+ green := 0.0,
+ blue := 0.0,
+ alpha := 0.0
+ }.
+
+alpha(#{alpha := SA}, #{alpha := DA}) ->
+ SA + DA*(1.0 - SA).
+
+red(#{red := SV, alpha := SA}, #{red := DV, alpha := DA}) ->
+ SV*SA + DV*DA*(1.0 - SA).
+green(#{green := SV, alpha := SA}, #{green := DV, alpha := DA}) ->
+ SV*SA + DV*DA*(1.0 - SA).
+blue(#{blue := SV, alpha := SA}, #{blue := DV, alpha := DA}) ->
+ SV*SA + DV*DA*(1.0 - SA).</code>
+ <p>Compile (file <c>color.erl</c>) and test:</p>
+ <pre>
+> <input>c(color).</input>
+{ok,color}
+> <input>C1 = color:new(0.3,0.4,0.5,1.0).</input>
+#{alpha => 1.0,blue => 0.5,green => 0.4,red => 0.3}
+> <input>C2 = color:new(1.0,0.8,0.1,0.3).</input>
+#{alpha => 0.3,blue => 0.1,green => 0.8,red => 1.0}
+> <input>color:blend(C1,C2).</input>
+#{alpha => 1.0,blue => 0.5,green => 0.4,red => 0.3}
+> <input>color:blend(C2,C1).</input>
+#{alpha => 1.0,blue => 0.38,green => 0.52,red => 0.51}
+</pre>
+ <p>This example warrant some explanation:</p>
+ <code type="none">
+-define(is_channel(V), (is_float(V) andalso V &gt;= 0.0 andalso V =&lt; 1.0)).</code>
+ <p>
+ First we define a macro <c>is_channel</c> to help with our guard tests.
+ This is only here for convenience and to reduce syntax cluttering.
+
+ You can read more about <seealso marker="doc/reference_manual:macros">Macros</seealso>
+ in the Erlang Reference Manual.
+ </p>
+ <code type="none">
+new(R,G,B,A) when ?is_channel(R), ?is_channel(G),
+ ?is_channel(B), ?is_channel(A) ->
+ #{red =&gt; R, green =&gt; G, blue =&gt; B, alpha =&gt; A}.</code>
+ <p>
+ The function <c>new/4</c> creates a new map term with and lets the keys
+ <c>red</c>, <c>green</c>, <c>blue</c> and <c>alpha</c> be associated
+ with an initial value. In this case we only allow for float
+ values between and including 0.0 and 1.0 as ensured by the <c>?is_channel/1</c> macro
+ for each argument. Only the <c>=></c> operator is allowed when creating a new map.
+ </p>
+ <p>
+ By calling <c>blend/2</c> on any color term created by <c>new/4</c> we can calculate
+ the resulting color as determined by the two maps terms.
+ </p>
+ <p>
+ The first thing <c>blend/2</c> does is to calculate the resulting alpha channel.
+ </p>
+ <code type="none">
+alpha(#{alpha := SA}, #{alpha := DA}) ->
+ SA + DA*(1.0 - SA).</code>
+ <p>
+ We fetch the value associated with key <c>alpha</c> for both arguments using
+ the <c>:=</c> operator. Any other keys
+ in the map are ignored, only the key <c>alpha</c> is required and checked for.
+ </p>
+ <p>This is also the case for functions <c>red/2</c>, <c>blue/2</c> and <c>green/2</c>.</p>
+ <code type="none">
+red(#{red := SV, alpha := SA}, #{red := DV, alpha := DA}) ->
+ SV*SA + DV*DA*(1.0 - SA).</code>
+ <p>
+ The difference here is that we check for two keys in each map argument. The other keys
+ are ignored.
+ </p>
+ <p>
+ Finally we return the resulting color in <c>blend/3</c>.
+ </p>
+ <code type="none">
+blend(Src,Dst,Alpha) when Alpha > 0.0 ->
+ Dst#{
+ red := red(Src,Dst) / Alpha,
+ green := green(Src,Dst) / Alpha,
+ blue := blue(Src,Dst) / Alpha,
+ alpha := Alpha
+ };</code>
+ <p>
+ We update the <c>Dst</c> map with new channel values. The syntax for updating an existing key with a new value is done with <c>:=</c> operator.
+ </p>
+ </section>
+
+ <section>
<title>Standard Modules and Manual Pages</title>
<p>Erlang has a lot of standard modules to help you do things. For
example, the module <c>io</c> contains a lot of functions to help
diff --git a/system/doc/installation_guide/Makefile b/system/doc/installation_guide/Makefile
index 6923f52d8a..83210bd21f 100644
--- a/system/doc/installation_guide/Makefile
+++ b/system/doc/installation_guide/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2012. All Rights Reserved.
+# Copyright Ericsson AB 1996-2014. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -58,8 +58,7 @@ XML_FILES = \
GENERATED_XML_FILES = \
INSTALL.xml \
INSTALL-CROSS.xml \
- INSTALL-WIN32.xml \
- MARKDOWN.xml
+ INSTALL-WIN32.xml
# ----------------------------------------------------
@@ -74,8 +73,7 @@ REDIRECT_HTML_DIR = $(HTMLDIR)/source
REDIRECT_HTML_FILES = \
$(REDIRECT_HTML_DIR)/INSTALL.html \
$(REDIRECT_HTML_DIR)/INSTALL-CROSS.html \
- $(REDIRECT_HTML_DIR)/INSTALL-WIN32.html \
- $(REDIRECT_HTML_DIR)/MARKDOWN.html
+ $(REDIRECT_HTML_DIR)/INSTALL-WIN32.html
# ----------------------------------------------------
# FLAGS
diff --git a/system/doc/installation_guide/install-binary.xml b/system/doc/installation_guide/install-binary.xml
index 07027a39f6..af7dab6e44 100644
--- a/system/doc/installation_guide/install-binary.xml
+++ b/system/doc/installation_guide/install-binary.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2013</year>
+ <year>2000</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,117 +31,40 @@
<rev>C</rev>
<file>install-binary.xml</file>
</header>
-
- <section>
- <title>UNIX</title>
-
- <section>
- <title>Introduction</title>
- <p>The system is delivered as a single compressed tar file.</p>
- <p>To browse the on-line HTML documentation, Netscape or an equivalent
- browser supporting frames is needed.</p>
- </section>
-
- <section>
- <title>Installation Procedure</title>
- <p>When installed, the entire system, except for a small start-up
- script, resides in a single directory tree. The location of this
- directory tree can be chosen arbitrarily by the installer, and it
- does not need to be in the user's <c>$PATH</c>. The only requirements
- are that the file system where it is placed has enough free space,
- and that the users who run Erlang/OTP have read access to it. In the
- example below, the directory tree is assumed to be located at
- <c>/usr/local/erlang</c>, which is here called the <em>top-level directory</em>.</p>
- <p>It is assumed that you have the compressed tar file, the name of
- which is <c><![CDATA[<PREFIX>.tar.gz]]></c>, where <c><![CDATA[<PREFIX>]]></c> is a string
- denoting the particular Erlang/OTP release, e.g.
- <c>otp_LXA_11930_sunos5_R9B</c>.</p>
- <p>Wherever the string <c><![CDATA[<PREFIX>]]></c> is used below, it should
- be replaced by the actual name prefix of the compressed tar file.</p>
- <p>The tape archive file does not have one single directory in which
- all other files are rooted. Therefore the tape archive file must be
- extracted into an empty (newly created) directory.</p>
- <list type="ordered">
- <item>
- <p>If the <em>top-level directory</em> does not already exist,
- create it:</p>
- <pre>
-mkdir /usr/local/erlang</pre>
- </item>
- <item>
- <p>Change the current directory to the <em>top level directory</em>:</p>
- <pre>
-cd /usr/local/erlang</pre>
- </item>
- <item>
- <p>Create the <em>installation directory</em> with an appropriate
- name. For example:</p>
- <pre>
-mkdir otp_r7b</pre>
- </item>
- <item>
- <p>Change to the <em>installation directory</em>, e.g.</p>
- <pre>
-cd otp_r7b</pre>
- </item>
- <item>
- <p>Assuming the compressed tar file resides in the directory
- <c><![CDATA[<SOME-DIR>]]></c>,. extract the compressed tar file into the
- current directory:</p>
- <pre>
-gunzip -c &lt;SOME-DIR&gt;/&lt;PREFIX&gt;.tar.gz | tar xfp -</pre>
- </item>
- <item>
- <p>Read the <c>README</c> file in the installation directory for
- last minute updates, before proceeding.</p>
- </item>
- <item>
- <p>Run the <c>Install</c> script in the installation directory,
- with the absolute path of the installation directory as argument,</p>
- <pre>
-./Install /usr/local/erlang/otp_r7b</pre>
- <p>and supply answers to the prompts.</p>
- <p>In most cases, there is a default answer in square brackets
- (<c>[]</c>). If the default is satisfactory, just press
- <c><![CDATA[<Return>]]></c>. In general you are only prompted for one thing:</p>
- <list type="bulleted">
- <item>
- <p>"Do you want to use a minimal system startup instead of the
- SASL startup?" <br></br>
-
- In a minimal system, only the Kernel and STDLIB applications
- are loaded and started. If the SASL startup is used, the SASL
- application is included as well. Normally, the minimal system
- is enough.</p>
- </item>
- </list>
- </item>
- <item>
- <p>Make Erlang/OTP available for users, either by putting the path
- <c>/usr/local/erlang/otp_r7b/bin</c> in users <c>$PATH</c>
- variable, or link the executable
- <c>/usr/local/erlang/otp_r7b/bin/erl</c> accordingly, e.g.:</p>
- <pre>
-ln -s /usr/local/erlang/otp_r7b/bin/erl /usr/local/bin/erl </pre>
- </item>
- </list>
- </section>
- </section>
-
<section>
<title>Windows</title>
<section>
<title>Introduction</title>
- <p>The system is delivered as a single <c>.exe</c> file.</p>
- <p>To browse the on-line HTML documentation, Netscape or an equivalent
- browser supporting frames is needed.</p>
+ <p>The system is delivered as a Windows Installer executable.
+ Get it from our <url href="http://www.erlang.org/download.html">download page</url>.</p>
</section>
<section>
- <title>Installation Procedure</title>
+ <title>Installation</title>
<p>The installation procedure is is automated. Double-click the
<c>.exe</c> file icon and follow the instructions.</p>
</section>
+ <section>
+ <title>Verification</title>
+ <list type="bulleted">
+ <item>
+ <p>Start Erlang/OTP by double-clicking on the Erlang shortcut icon on the
+ desktop.</p>
+ <p>Expect a command line window to pop up with an output looking something like this:</p>
+ <pre>
+ Erlang/OTP 17 [erts-6.0] [64-bit] [smp:2:2]
+
+ Eshell V6.0 (abort with ^G)
+ 1></pre>
+ </item>
+ <item>
+ <p>Exit by entering the command <c>halt()</c>,</p>
+ <pre>
+ 2> <input>halt().</input></pre>
+ <p>which will close the Erlang/OTP shell. </p>
+ </item>
+ </list>
+ </section>
</section>
</chapter>
diff --git a/system/doc/installation_guide/otp_version.xml b/system/doc/installation_guide/otp_version.xml
deleted file mode 100644
index 0ce5144160..0000000000
--- a/system/doc/installation_guide/otp_version.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2014</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>OTP version</title>
- <prepared></prepared>
- <responsible></responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date>2014-02-19</date>
- <rev></rev>
- <file>otp_version.xml</file>
- </header>
- <p>As of OTP release 17, the OTP release number corresponds to
- the major part of the OTP version. The OTP version as a concept was
- introduced in OTP 17. In the normal case, the OTP version will be
- constructed as
- <c>&lt;Major&gt;.&lt;Minor&gt;.&lt;Patch&gt;</c> where <c>&lt;Major&gt;</c>
- is the most significant part. However, more dot separated parts than
- this may exist. The dot separated parts consists of integers. If all
- parts less significant than <c>&lt;Minor&gt;</c> equals <c>0</c>, they
- are omitted. The three normal parts
- <c>&lt;Major&gt;.&lt;Minor&gt;.&lt;Patch&gt;</c> will be changed as
- follows:</p>
- <taglist>
- <tag><c>&lt;Major&gt;</c></tag><item>Increased when major changes,
- including incompatibilities, have been made.</item>
- <tag><c>&lt;Minor&gt;</c></tag><item>Increased when new functionality
- has been added.</item>
- <tag><c>&lt;Patch&gt;</c></tag><item>Increased when pure bug fixes
- have been made.</item>
- </taglist>
- <p>When a part in the version number is increased, all less significant
- parts are set to <c>0</c>. Release candidates have an <c>-rc&lt;N&gt;</c>
- suffix. The suffix <c>-rc0</c> will be used during development up to
- the first release candidate.</p>
-
- <p>OTP of a specific version is a set of applications of
- specific versions. The application versions identified by
- an OTP version corresponds to application versions that
- have been tested together by the Erlang/OTP team at Ericsson AB.
- An OTP system can however be put together with applications from
- different OTP versions. Such a combination of application versions
- has not been tested by the Erlang/OTP team. It is therefore
- <em>always preferred to use OTP applications from one single OTP
- version</em>.</p>
-
- <p>Application versions will be managed the same way as the OTP version.
- Application versions part of a release candidate will however not have an
- <c>-rc&lt;N&gt;</c> suffix as the OTP version. Also note that a major
- increment in an application version does not necessarily imply a major
- increment of the OTP version. This depends on whether the
- major change in the application is considered as a major change for
- OTP as a whole or not.</p>
-
- <p>In an OTP source code tree as well as in an installed OTP
- development system, the OTP version can be read from the text
- file <c>OTP_VERSION</c> in the OTP installation root directory
- (<seealso marker="kernel:code#root_dir/0"><c>code:root_dir()</c></seealso>).</p>
- <p>If the version read from the <c>OTP_VERSION</c> file in a
- development system has a <c>**</c> suffix, the system has been
- patched using the <c>$ERL_TOP/otp_build patch_app</c> tool. In
- this case, the system consists of application versions from
- multiple OTP versions. The version preceding the <c>**</c>
- suffix corresponds to the OTP version of the base system that
- has been patched. Note that if a development system is updated by
- other means than <c>$ERL_TOP/otp_build patch_app</c>, the
- <c>OTP_VERSION</c> file may identify wrong OTP version.</p>
-
- <p>On a target system (see the
- <seealso marker="doc/system_principles:create_target">system principles</seealso>
- documentation) no <c>OTP_VERSION</c> file will exist. This since
- one easily can create a target system where it is hard to even
- determine the base OTP version.</p>
-</chapter>
-
diff --git a/system/doc/installation_guide/part.xml b/system/doc/installation_guide/part.xml
index 150df39512..02bf98db7c 100644
--- a/system/doc/installation_guide/part.xml
+++ b/system/doc/installation_guide/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2000</year><year>2013</year>
+ <year>2000</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,11 +31,8 @@
<description>
<p>How to install Erlang/OTP on UNIX or Windows.</p>
</description>
- <xi:include href="otp_version.xml"/>
<xi:include href="install-binary.xml"/>
- <xi:include href="verification.xml"/>
<xi:include href="INSTALL.xml"/>
<xi:include href="INSTALL-CROSS.xml"/>
<xi:include href="INSTALL-WIN32.xml"/>
- <xi:include href="MARKDOWN.xml"/>
</part> \ No newline at end of file
diff --git a/system/doc/installation_guide/verification.xml b/system/doc/installation_guide/verification.xml
deleted file mode 100644
index 391ddfb7b8..0000000000
--- a/system/doc/installation_guide/verification.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2000</year><year>2013</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Installation Verification</title>
- <prepared>Peter H&ouml;gfeldt</prepared>
- <responsible>Peter H&ouml;gfeldt</responsible>
- <docno></docno>
- <approved>(Peter H&ouml;gfeldt</approved>
- <checked></checked>
- <date>1997-05-26</date>
- <rev>C</rev>
- <file>verification.xml</file>
- </header>
- <p>This chapter is about verifying your installation by performing
- a few simple tests to see that your system is properly installed.</p>
-
- <section>
- <title>UNIX</title>
- <list type="bulleted">
- <item>
- <p>Start Erlang/OTP from the command line,</p>
- <pre>
- unix> erl</pre>
- <p>Expect the following output:</p>
- <pre>
- Erlang (BEAM) emulator version 5.0.1 [threads]
-
- Eshell V5.0.1 (abort with ^G)
- 1></pre>
- </item>
- <item>
- <p>Start the GS-based toolbar from the Erlang shell, </p>
- <pre>
- 1> <input>toolbar:start().</input></pre>
- <p>and check that the toolbar window pops up.
- </p>
- <p><em>Note:</em> The trailing full stop (<c>"."</c>) is an end marker
- for all commands in the Erlang shell, and must be entered for a
- command to begin execution.</p>
- </item>
- <item>
- <p>Exit by entering the command <c>halt()</c>,</p>
- <pre>
- 2> <input>halt().</input></pre>
- <p>which should end both the toolbar window and the command line window. </p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Windows</title>
- <list type="bulleted">
- <item>
- <p>Start Erlang/OTP by double-clicking on the Erlang shortcut icon on the
- desktop.</p>
- <p>Expect a command line window to pop up with the following output,</p>
- <pre>
- Erlang (BEAM) emulator version 5.0.1 [threads]
-
- Eshell V5.0.1 (abort with ^G)
- 1></pre>
- </item>
- <item>
- <p>Start the GS-based toolbar from the Erlang shell, </p>
- <pre>
- 1> <input>toolbar:start().</input></pre>
- <p>and check that the toolbar window pops up.
- </p>
- <p><em>Note:</em> The trailing full stop (<c>"."</c>) is an end marker
- for all commands in the Erlang shell, and must be entered for a
- command to begin execution.</p>
- </item>
- <item>
- <p>Exit by entering the command <c>halt()</c>,</p>
- <pre>
- 2> <input>halt().</input></pre>
- <p>which should end both the toolbar window and the command line window. </p>
- </item>
- </list>
- </section>
-</chapter>
-
diff --git a/system/doc/installation_guide/xmlfiles.mk b/system/doc/installation_guide/xmlfiles.mk
index 245491ab94..c443334cd7 100644
--- a/system/doc/installation_guide/xmlfiles.mk
+++ b/system/doc/installation_guide/xmlfiles.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2013. All Rights Reserved.
+# Copyright Ericsson AB 2009-2014. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -17,10 +17,7 @@
# %CopyrightEnd%
#
INST_GUIDE_CHAPTER_FILES = \
- otp_version.xml \
install-binary.xml \
- verification.xml \
INSTALL.xml \
INSTALL-CROSS.xml \
- INSTALL-WIN32.xml \
- MARKDOWN.xml
+ INSTALL-WIN32.xml
diff --git a/system/doc/reference_manual/character_set.xml b/system/doc/reference_manual/character_set.xml
new file mode 100644
index 0000000000..884898eb34
--- /dev/null
+++ b/system/doc/reference_manual/character_set.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2014</year><year>2014</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Character Set and Source File Encoding</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>character_set.xml</file>
+ </header>
+
+ <section>
+ <title>Character Set</title>
+ <p>In Erlang 4.8/OTP R5A the syntax of Erlang tokens was extended to
+ allow the use of the full ISO-8859-1 (Latin-1) character set. This
+ is noticeable in the following ways:</p>
+ <list type="bulleted">
+ <item>
+ <p>All the Latin-1 printable characters can be used and are
+ shown without the escape backslash convention.</p>
+ </item>
+ <item>
+ <p>Atoms and variables can use all Latin-1 letters.</p>
+ </item>
+ </list>
+ <table>
+ <row>
+ <cell align="left" valign="middle"><em>Octal</em></cell>
+ <cell align="left" valign="middle"><em>Decimal</em></cell>
+ <cell align="left" valign="middle">&nbsp;</cell>
+ <cell align="left" valign="middle"><em>Class</em></cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">200 - 237</cell>
+ <cell align="left" valign="middle">128 - 159</cell>
+ <cell align="left" valign="middle">&nbsp;</cell>
+ <cell align="left" valign="middle">Control characters</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">240 - 277</cell>
+ <cell align="left" valign="middle">160 - 191</cell>
+ <cell align="right" valign="middle">- &iquest;</cell>
+ <cell align="left" valign="middle">Punctuation characters</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">300 - 326</cell>
+ <cell align="left" valign="middle">192 - 214</cell>
+ <cell align="center" valign="middle">&Agrave; - &Ouml;</cell>
+ <cell align="left" valign="middle">Uppercase letters</cell>
+ </row>
+ <row>
+ <cell align="center" valign="middle">327</cell>
+ <cell align="center" valign="middle">215</cell>
+ <cell align="center" valign="middle">&times;</cell>
+ <cell align="left" valign="middle">Punctuation character</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">330 - 336</cell>
+ <cell align="left" valign="middle">216 - 222</cell>
+ <cell align="center" valign="middle">&Oslash; - &THORN;</cell>
+ <cell align="left" valign="middle">Uppercase letters</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">337 - 366</cell>
+ <cell align="left" valign="middle">223 - 246</cell>
+ <cell align="center" valign="middle">&szlig; - &ouml;</cell>
+ <cell align="left" valign="middle">Lowercase letters</cell>
+ </row>
+ <row>
+ <cell align="center" valign="middle">367</cell>
+ <cell align="center" valign="middle">247</cell>
+ <cell align="center" valign="middle">&divide;</cell>
+ <cell align="left" valign="middle">Punctuation character</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">370 - 377</cell>
+ <cell align="left" valign="middle">248 - 255</cell>
+ <cell align="center" valign="middle">&oslash; - &yuml;</cell>
+ <cell align="left" valign="middle">Lowercase letters</cell>
+ </row>
+ <tcaption>Character Classes.</tcaption>
+ </table>
+ <p>In Erlang/OTP R16B the syntax of Erlang tokens was extended to
+ handle Unicode. To begin with the support is limited to
+ strings, but Erlang/OTP 18 is expected to handle Unicode atoms
+ as well. More about the usage of Unicode in Erlang source files
+ can be found in <seealso
+ marker="stdlib:unicode_usage#unicode_in_erlang">STDLIB's User's
+ Guide</seealso>.</p>
+ </section>
+ <section>
+ <title>Source File Encoding</title>
+ <p>The Erlang source file <marker
+ id="encoding">encoding</marker> is selected by a
+ comment in one of the first two lines of the source file. The
+ first string that matches the regular expression
+ <c>coding\s*[:=]\s*([-a-zA-Z0-9])+</c> selects the encoding. If
+ the matching string is not a valid encoding it is ignored. The
+ valid encodings are <c>Latin-1</c> and <c>UTF-8</c> where the
+ case of the characters can be chosen freely.</p>
+ <p>The following example selects UTF-8 as default encoding:</p>
+ <pre>
+%% coding: utf-8</pre>
+ <p>Two more examples, both selecting Latin-1 as default encoding:</p>
+ <pre>
+%% For this file we have chosen encoding = Latin-1</pre>
+ <pre>
+%% -*- coding: latin-1 -*-</pre>
+ <p>The default encoding for Erlang source files was changed from
+ Latin-1 to UTF-8 in Erlang OTP 17.0.</p>
+ </section>
+</chapter>
diff --git a/system/doc/reference_manual/data_types.xml b/system/doc/reference_manual/data_types.xml
index 8c690d6b86..0031664dfb 100644
--- a/system/doc/reference_manual/data_types.xml
+++ b/system/doc/reference_manual/data_types.xml
@@ -190,6 +190,38 @@ adam
</section>
<section>
+ <title>Map</title>
+ <p>Compound data type with a variable number of key-value associations:</p>
+ <pre>
+#{Key1=>Value1,...,KeyN=>ValueN}</pre>
+ <p>Each key-value association in the map is called an
+ <em>association pair</em>. The key and value parts of the pair are
+ called <em>elements</em>. The number of association pairs is said to be
+ the <em>size</em> of the map.</p>
+ <p>There exists a number of BIFs to manipulate maps.</p>
+ <p>Examples:</p>
+ <pre>
+1> <input>M1 = #{name=>adam,age=>24,date=>{july,29}}.</input>
+#{age => 24,date => {july,29},name => adam}
+2> <input>maps:get(name,M1).</input>
+adam
+3> <input>maps:get(date,M1).</input>
+{july,29}
+4> <input>M2 = maps:update(age,25,M1).</input>
+#{age => 25,date => {july,29},name => adam}
+5> <input>map_size(M).</input>
+3
+6> <input>map_size(#{}).</input>
+0</pre>
+ <p>A collection of maps processing functions can be found in
+ the STDLIB module <seealso marker="stdlib:maps"><c>maps</c></seealso>.</p>
+ <p>Read more about <seealso marker="maps">Maps</seealso>.</p>
+ <note>
+ <p>Maps are considered experimental during OTP 17.</p>
+ </note>
+ </section>
+
+ <section>
<title>List</title>
<p>Compound data type with a variable number of terms.</p>
<pre>
diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml
index e9de3e006e..0ca425da86 100644
--- a/system/doc/reference_manual/expressions.xml
+++ b/system/doc/reference_manual/expressions.xml
@@ -56,7 +56,7 @@ Expr1 + Expr2</code>
<marker id="term"></marker>
<title>Terms</title>
<p>The simplest form of expression is a term, that is an integer,
- float, atom, string, list or tuple.
+ float, atom, string, list, map or tuple.
The return value is the term itself.</p>
</section>
@@ -792,6 +792,244 @@ Expr1 -- Expr2</pre>
</section>
<section>
+ <title>Map Expressions</title>
+ <section>
+ <title>Creating Maps</title>
+ <p>
+ Constructing a new map is done by letting an expression <c>K</c> be associated with
+ another expression <c>V</c>:
+ </p>
+ <code>#{ K => V }</code>
+ <p>
+ New maps may include multiple associations at construction by listing every
+ association:
+ </p>
+ <code>#{ K1 => V1, .., Kn => Vn }</code>
+ <p>
+ An empty map is constructed by not associating any terms with each other:
+ </p>
+ <code>#{}</code>
+ <p>
+ All keys and values in the map are terms. Any expression is first evaluated and
+ then the resulting terms are used as <em>key</em> and <em>value</em> respectively.
+ </p>
+ <p>
+ Keys and values are separated by the <c>=></c> arrow and associations are
+ separated by <c>,</c>.
+ </p>
+
+ <p>
+ Examples:
+ </p>
+ <code>
+M0 = #{}, % empty map
+M1 = #{a => &lt;&lt;"hello"&gt;&gt;}, % single association with literals
+M2 = #{1 => 2, b => b}, % multiple associations with literals
+M3 = #{k => {A,B}}, % single association with variables
+M4 = #{{"w", 1} => f()}. % compound key associated with an evaluated expression</code>
+ <p>
+ where, <c>A</c> and <c>B</c> are any expressions and <c>M0</c> through <c>M4</c>
+ are the resulting map terms.
+ </p>
+ <p>
+ If two matching keys are declared, the latter key will take precedence.
+ </p>
+ <p>
+ Example:
+ </p>
+
+<pre>
+1> <input>#{1 => a, 1 => b}.</input>
+#{1 => b }
+2> <input>#{1.0 => a, 1 => b}.</input>
+#{1 => b, 1.0 => a}
+</pre>
+ <p>
+ The order in which the expressions constructing the keys and their
+ associated values are evaluated is not defined. The syntactic order of
+ the key-value pairs in the construction is of no relevance, except in
+ the above mentioned case of two matching keys.
+ </p>
+ </section>
+
+ <section>
+ <title>Updating Maps</title>
+ <p>
+ Updating a map has similar syntax as constructing it.
+ </p>
+ <p>
+ An expression defining the map to be updated is put in front of the expression
+ defining the keys to be updated and their respective values.
+ </p>
+ <code>M#{ K => V }</code>
+ <p>
+ where <c>M</c> is a term of type map and <c>K</c> and <c>V</c> are any expression.
+ </p>
+ <p>
+ If key <c>K</c> does not match any existing key in the map, a new association
+ will be created from key <c>K</c> to value <c>V</c>. If key <c>K</c> matches
+ an existing key in map <c>M</c> its associated value will be replaced by the
+ new value <c>V</c>. In both cases the evaluated map expression will return a new map.
+ </p>
+ <p>
+ If <c>M</c> is not of type map an exception of type <c>badmap</c> is thrown.
+ </p>
+ <p>
+ To only update an existing value, the following syntax is used,
+ </p>
+ <code>M#{ K := V } </code>
+ <p>
+ where <c>M</c> is an term of type map, <c>V</c> is an expression and <c>K</c>
+ is an expression which evaluates to an existing key in <c>M</c>.
+ </p>
+ <p>
+ If key <c>K</c> does not match any existing keys in map <c>M</c> an exception
+ of type <c>badarg</c> will be triggered at runtime. If a matching key <c>K</c>
+ is present in map <c>M</c> its associated value will be replaced by the new
+ value <c>V</c> and the evaluated map expression returns a new map.
+ </p>
+ <p>
+ If <c>M</c> is not of type map an exception of type <c>badmap</c> is thrown.
+ </p>
+ <p>
+ Examples:
+ </p>
+ <code>
+M0 = #{},
+M1 = M0#{a => 0},
+M2 = M1#{a => 1, b => 2},
+M3 = M2#{"function" => fun() -> f() end},
+M4 = M3#{a := 2, b := 3}. % 'a' and 'b' was added in `M1` and `M2`.</code>
+ <p>
+ where <c>M0</c> is any map. It follows that <c>M1 .. M4</c> are maps as well.
+ </p>
+ <p>
+ More Examples:
+ </p>
+<pre>
+1> <input>M = #{1 => a}.</input>
+#{1 => a }
+2> <input>M#{1.0 => b}.</input>
+#{1 => a, 1.0 => b}.
+3> <input>M#{1 := b}.</input>
+#{1 => b}
+4> <input>M#{1.0 := b}.</input>
+** exception error: bad argument
+</pre>
+ <p>
+ As in construction, the order in which the key and value expressions
+ are evaluated is not defined. The
+ syntactic order of the key-value pairs in the update is of no
+ relevance, except in the case where two keys match, in which
+ case the latter value is used.
+ </p>
+ </section>
+
+ <section>
+ <title>Maps in Patterns</title>
+ <p>
+ Matching of key-value associations from maps is done in the following way:
+ </p>
+
+ <code>#{ K := V } = M</code>
+ <p>
+ where <c>M</c> is any map. The key <c>K</c> has to be an expression with bound
+ variables or a literals, and <c>V</c> can be any pattern with either bound or
+ unbound variables.
+ </p>
+ <p>
+ If the variable <c>V</c> is unbound, it will be bound to the value associated
+ with the key <c>K</c>, which has to exist in the map <c>M</c>. If the variable
+ <c>V</c> is bound, it has to match the value associated with <c>K</c> in <c>M</c>.
+ </p>
+ <p> Example: </p>
+<code>
+1> <input>M = #{"tuple" => {1,2}}.</input>
+#{"tuple" => {1,2}}
+2> <input>#{"tuple" := {1,B}} = M.</input>
+#{"tuple" => {1,2}}
+3> <input>B.</input>
+2.</code>
+ <p>
+ This will bind variable <c>B</c> to integer <c>2</c>.
+ </p>
+ <p>
+ Similarly, multiple values from the map may be matched:
+ </p>
+ <code>#{ K1 := V1, .., Kn := Vn } = M</code>
+ <p>
+ where keys <c>K1 .. Kn</c> are any expressions with literals or bound variables. If all
+ keys exist in map <c>M</c> all variables in <c>V1 .. Vn</c> will be matched to the
+ associated values of their respective keys.
+ </p>
+ <p>
+ If the matching conditions are not met, the match will fail, either with
+ </p>
+ <list>
+ <item>
+ a <c>badmatch</c> exception, if used in the context of the matching operator
+ as in the example,
+ </item>
+ <item>
+ or resulting in the next clause being tested in function heads and
+ case expressions.
+ </item>
+ </list>
+ <p>
+ Matching in maps only allows for <c>:=</c> as delimiters of associations.
+ The order in which keys are declared in matching has no relevance.
+ </p>
+ <p>
+ Duplicate keys are allowed in matching and will match each pattern associated
+ to the keys.
+ </p>
+ <code>#{ K := V1, K := V2 } = M</code>
+ <p>
+ Matching an expression against an empty map literal will match its type but
+ no variables will be bound:
+ </p>
+ <code>#{} = Expr</code>
+ <p>
+ This expression will match if the expression <c>Expr</c> is of type map, otherwise
+ it will fail with an exception <c>badmatch</c>.
+ </p>
+ <section>
+ <title>Matching syntax: Example with literals in function heads</title>
+ <p>
+ Matching of literals as keys are allowed in function heads.
+ </p>
+ <code>
+%% only start if not_started
+handle_call(start, From, #{ state := not_started } = S) ->
+...
+ {reply, ok, S#{ state := start }};
+
+%% only change if started
+handle_call(change, From, #{ state := start } = S) ->
+...
+ {reply, ok, S#{ state := changed }};</code>
+ </section>
+ </section>
+ <section>
+ <title>Maps in Guards</title>
+ <p>
+ Maps are allowed in guards as long as all sub-expressions are valid guard expressions.
+ </p>
+ <p>
+ Two guard BIFs handles maps:
+ </p>
+ <list>
+ <item>
+ <seealso marker="erts:erlang#is_map/1">is_map/1</seealso>
+ </item>
+ <item>
+ <seealso marker="erts:erlang#map_size/1">map_size/1</seealso>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
<marker id="bit_syntax"></marker>
<title>Bit Syntax Expressions</title>
<code type="none"><![CDATA[<<>>
@@ -1348,6 +1586,9 @@ end</pre>
<cell align="left" valign="middle"><c>is_list/1</c></cell>
</row>
<row>
+ <cell align="left" valign="middle"><c>is_map/1</c></cell>
+ </row>
+ <row>
<cell align="left" valign="middle"><c>is_number/1</c></cell>
</row>
<row>
@@ -1398,6 +1639,9 @@ end</pre>
<cell align="left" valign="middle"><c>length(List)</c></cell>
</row>
<row>
+ <cell align="left" valign="middle"><c>map_size(Map)</c></cell>
+ </row>
+ <row>
<cell align="left" valign="middle"><c>node()</c></cell>
</row>
<row>
diff --git a/system/doc/reference_manual/introduction.xml b/system/doc/reference_manual/introduction.xml
index aa42967625..36bec17825 100644
--- a/system/doc/reference_manual/introduction.xml
+++ b/system/doc/reference_manual/introduction.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2013</year>
+ <year>2003</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -79,88 +79,5 @@
when xor</p>
</section>
- <section>
- <title>Character Set</title>
- <p>In Erlang 4.8/OTP R5A the syntax of Erlang tokens was extended to
- allow the use of the full ISO-8859-1 (Latin-1) character set. This
- is noticeable in the following ways:</p>
- <list type="bulleted">
- <item>
- <p>All the Latin-1 printable characters can be used and are
- shown without the escape backslash convention.</p>
- </item>
- <item>
- <p>Atoms and variables can use all Latin-1 letters.</p>
- </item>
- </list>
- <table>
- <row>
- <cell align="left" valign="middle"><em>Octal</em></cell>
- <cell align="left" valign="middle"><em>Decimal</em></cell>
- <cell align="left" valign="middle">&nbsp;</cell>
- <cell align="left" valign="middle"><em>Class</em></cell>
- </row>
- <row>
- <cell align="left" valign="middle">200 - 237</cell>
- <cell align="left" valign="middle">128 - 159</cell>
- <cell align="left" valign="middle">&nbsp;</cell>
- <cell align="left" valign="middle">Control characters</cell>
- </row>
- <row>
- <cell align="left" valign="middle">240 - 277</cell>
- <cell align="left" valign="middle">160 - 191</cell>
- <cell align="right" valign="middle">- &iquest;</cell>
- <cell align="left" valign="middle">Punctuation characters</cell>
- </row>
- <row>
- <cell align="left" valign="middle">300 - 326</cell>
- <cell align="left" valign="middle">192 - 214</cell>
- <cell align="center" valign="middle">&Agrave; - &Ouml;</cell>
- <cell align="left" valign="middle">Uppercase letters</cell>
- </row>
- <row>
- <cell align="center" valign="middle">327</cell>
- <cell align="center" valign="middle">215</cell>
- <cell align="center" valign="middle">&times;</cell>
- <cell align="left" valign="middle">Punctuation character</cell>
- </row>
- <row>
- <cell align="left" valign="middle">330 - 336</cell>
- <cell align="left" valign="middle">216 - 222</cell>
- <cell align="center" valign="middle">&Oslash; - &THORN;</cell>
- <cell align="left" valign="middle">Uppercase letters</cell>
- </row>
- <row>
- <cell align="left" valign="middle">337 - 366</cell>
- <cell align="left" valign="middle">223 - 246</cell>
- <cell align="center" valign="middle">&szlig; - &ouml;</cell>
- <cell align="left" valign="middle">Lowercase letters</cell>
- </row>
- <row>
- <cell align="center" valign="middle">367</cell>
- <cell align="center" valign="middle">247</cell>
- <cell align="center" valign="middle">&divide;</cell>
- <cell align="left" valign="middle">Punctuation character</cell>
- </row>
- <row>
- <cell align="left" valign="middle">370 - 377</cell>
- <cell align="left" valign="middle">248 - 255</cell>
- <cell align="center" valign="middle">&oslash; - &yuml;</cell>
- <cell align="left" valign="middle">Lowercase letters</cell>
- </row>
- <tcaption>Character Classes.</tcaption>
- </table>
- <p>In Erlang/OTP R16 the syntax of Erlang tokens was extended to
- handle Unicode. To begin with the support is limited to strings,
- but Erlang/OTP R18 is expected to handle Unicode atoms as well.
- More about the usage of Unicode in Erlang source files can be
- found in <seealso
- marker="stdlib:unicode_usage#unicode_in_erlang">STDLIB's User'S
- Guide</seealso>. The default encoding for Erlang source files
- is still Latin-1, but in Erlang/OTP R17 the default encoding
- will be UTF-8. The details on how to state the encoding of an
- Erlang source file can be found in <seealso
- marker="stdlib:epp#encoding">epp(3)</seealso>.</p>
- </section>
</chapter>
diff --git a/system/doc/reference_manual/modules.xml b/system/doc/reference_manual/modules.xml
index 9e5f4de385..f0ec7ef165 100644
--- a/system/doc/reference_manual/modules.xml
+++ b/system/doc/reference_manual/modules.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2013</year>
+ <year>2003</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -53,10 +53,10 @@ fact(0) -> % |
<pre>
-Tag(Value).</pre>
<p><c>Tag</c> must be an atom, while <c>Value</c> must be a literal
- term. As a convenience in user-defined attributes, the literal term
- <c>Value</c> the syntax <c>Name/Arity</c>
- (where <c>Name</c> is an atom and <c>Arity</c> a positive integer)
- will be translated to <c>{Name,Arity}</c>.</p>
+ term. As a convenience in user-defined attributes, if the literal term
+ <c>Value</c> has the syntax <c>Name/Arity</c>
+ (where <c>Name</c> is an atom and <c>Arity</c> a positive integer),
+ the term <c>Name/Arity</c> will be translated to <c>{Name,Arity}</c>.</p>
<p>Any module attribute can be specified. The attributes are stored
in the compiled code and can be retrieved by calling
@@ -134,8 +134,21 @@ fact(0) -> % |
standard behaviours <c>gen_server</c>, <c>gen_fsm</c>,
<c>gen_event</c> or <c>supervisor</c>.</p>
<p>The spelling <c>behavior</c> is also accepted.</p>
- <p>Read more about behaviours and callback modules in OTP Design
- Principles.</p>
+ <p>The callback functions of the module can be specified either
+ directly by the exported function <c>behaviour_info/1</c>:</p>
+ <pre>
+behaviour_info(callbacks) -> Callbacks.</pre>
+ <p>or by a <c>-callback</c> attribute for each callback
+ function:</p>
+ <pre>
+-callback Name(Arguments) -> Result.</pre>
+ <p>where <c>Arguments</c> is a list of zero or more arguments.
+ The <c>-callback</c> attribute is to be preferred since the
+ extra type information can be used by tools to produce
+ documentation or find discrepancies.</p>
+ <p>Read more about behaviours and callback modules in
+ <seealso marker="doc/design_principles:spec_proc#behaviours">
+ OTP Design Principles</seealso>.</p>
</section>
<section>
diff --git a/system/doc/reference_manual/part.xml b/system/doc/reference_manual/part.xml
index b4f114c268..ee8f3dd7eb 100644
--- a/system/doc/reference_manual/part.xml
+++ b/system/doc/reference_manual/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2003</year><year>2013</year>
+ <year>2003</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,6 +28,7 @@
<rev></rev>
</header>
<xi:include href="introduction.xml"/>
+ <xi:include href="character_set.xml"/>
<xi:include href="data_types.xml"/>
<xi:include href="patterns.xml"/>
<xi:include href="modules.xml"/>
diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml
index 71aec732cf..cc35c6eb21 100644
--- a/system/doc/reference_manual/typespec.xml
+++ b/system/doc/reference_manual/typespec.xml
@@ -100,6 +100,7 @@
| Fun
| Integer
| List
+ | Map
| Tuple
| Union
| UserDefined %% described in Section 6.3
@@ -126,10 +127,17 @@
| 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 a map of any size
+ | #{PairList}
+
Tuple :: tuple() %% stands for a tuple of any size
| {}
| {TList}
+ PairList :: Type => Type
+ | Type => Type, PairList
+
TList :: Type
| Type, TList
@@ -275,6 +283,10 @@
Records have been extended to possibly contain type information.
This is described in the sub-section <seealso marker="#typeinrecords">"Type information in record declarations"</seealso> below.
</p>
+ <note>
+ <p>Map types, both <c>map()</c> and <c>#{ ... }</c>, are considered experimental during OTP 17.</p>
+ <p>No type information of maps pairs, only the containing map types, are used by Dialyzer in OTP 17.</p>
+ </note>
</section>
<section>
diff --git a/system/doc/system_principles/create_target.xmlsrc b/system/doc/system_principles/create_target.xmlsrc
index fbc935d708..b5f8d8ac4d 100644
--- a/system/doc/system_principles/create_target.xmlsrc
+++ b/system/doc/system_principles/create_target.xmlsrc
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2002</year><year>2013</year>
+ <year>2002</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -21,7 +21,7 @@
</legalnotice>
- <title>Creating a First Target System</title>
+ <title>Creating and Upgrading a Target System</title>
<prepared>Peter H&ouml;gfeldt</prepared>
<responsible></responsible>
<docno></docno>
@@ -71,23 +71,24 @@
</section>
<section>
+ <marker id="create"/>
<title>Creating a Target System</title>
<p>It is assumed that you have a working Erlang/OTP system structured
according to the OTP Design Principles.</p>
- <p><em>Step 1.</em> First create a <c>.rel</c> file (see
- <c>rel(4)</c>) that specifies the <c>erts</c> version
- and lists all applications that should be included in the new
- basic target system. An example is the following
+ <p><em>Step 1.</em> First create a <c>.rel</c> file (see <seealso
+ marker="sasl:rel">rel(4)</seealso>) that specifies the <c>erts</c>
+ version and lists all applications that should be included in the
+ new basic target system. An example is the following
<c>mysystem.rel</c> file:</p>
<code type="none">
%% mysystem.rel
{release,
{"MYSYSTEM", "FIRST"},
- {erts, "5.1"},
- [{kernel, "2.7"},
- {stdlib, "1.10"},
- {sasl, "1.9.3"},
- {pea, "1.0"}]}. </code>
+ {erts, "5.10.4"},
+ [{kernel, "2.16.4"},
+ {stdlib, "1.19.4"},
+ {sasl, "2.3.4"},
+ {pea, "1.0"}]}.</code>
<p>The listed applications are not only original Erlang/OTP
applications but possibly also new applications that you have
written yourself (here examplified by the application
@@ -116,13 +117,13 @@ os> <input>erl -pa /home/user/target_system/myapps/pea-1.0/ebin</input></pre>
<c>systools:make_tar/2</c>. That file has the following
contents:</p>
<code type="none">
-erts-5.1/bin/
+erts-5.10.4/bin/
releases/FIRST/start.boot
releases/FIRST/mysystem.rel
releases/mysystem.rel
-lib/kernel-2.7/
-lib/stdlib-1.10/
-lib/sasl-1.9.3/
+lib/kernel-2.16.4/
+lib/stdlib-1.19.4/
+lib/sasl-2.3.4/
lib/pea-1.0/ </code>
<p>The file <c>releases/FIRST/start.boot</c> is a copy of our
<c>mysystem.boot</c></p>
@@ -142,16 +143,19 @@ lib/pea-1.0/ </code>
<item>Creates the temporary directory <c>tmp</c> and extracts the tar file
<c>mysystem.tar.gz</c> into that directory. </item>
<item>Deletes the <c>erl</c> and <c>start</c> files from
- <c>tmp/erts-5.1/bin</c>. These files will be created again from
+ <c>tmp/erts-5.10.4/bin</c>. These files will be created again from
source when installing the release.</item>
<item>Creates the directory <c>tmp/bin</c>.</item>
<item>Copies the previously created file <c>plain.boot</c> to
<c>tmp/bin/start.boot</c>.</item>
<item>Copies the files <c>epmd</c>, <c>run_erl</c>, and
- <c>to_erl</c> from the directory <c>tmp/erts-5.1/bin</c> to
+ <c>to_erl</c> from the directory <c>tmp/erts-5.10.4/bin</c> to
the directory <c>tmp/bin</c>.</item>
+ <item>Creates the directory <c>tmp/log</c>, which will be used
+ if the system is started as embedded with the <c>bin/start</c>
+ script.</item>
<item>Creates the file <c>tmp/releases/start_erl.data</c> with
- the contents "5.1 FIRST". This file is to be passed as data
+ the contents "5.10.4 FIRST". This file is to be passed as data
file to the <c>start_erl</c> script.
</item>
<item>Recreates the file <c>mysystem.tar.gz</c> from the directories
@@ -171,11 +175,11 @@ lib/pea-1.0/ </code>
<item>Extracts the tar file <c>mysystem.tar.gz</c> into the target
directory <c>/usr/local/erl-target</c>.</item>
<item>In the target directory reads the file <c>releases/start_erl.data</c>
- in order to find the Erlang runtime system version ("5.1").</item>
+ in order to find the Erlang runtime system version ("5.10.4").</item>
<item>Substitutes <c>%FINAL_ROOTDIR%</c> and <c>%EMU%</c> for
<c>/usr/local/erl-target</c> and <c>beam</c>, respectively, in
the files <c>erl.src</c>, <c>start.src</c>, and
- <c>start_erl.src</c> of the target <c>erts-5.1/bin</c>
+ <c>start_erl.src</c> of the target <c>erts-5.10.4/bin</c>
directory, and puts the resulting files <c>erl</c>,
<c>start</c>, and <c>run_erl</c> in the target <c>bin</c>
directory.</item>
@@ -185,6 +189,7 @@ lib/pea-1.0/ </code>
</section>
<section>
+ <marker id="start"/>
<title>Starting a Target System</title>
<p>Now we have a target system that can be started in various ways.</p>
<p>We start it as a <em>basic target system</em> by invoking</p>
@@ -193,7 +198,7 @@ os> <input>/usr/local/erl-target/bin/erl</input></pre>
<p>where only the <c>kernel</c> and <c>stdlib</c> applications are
started, i.e. the system is started as an ordinary development
system. There are only two files needed for all this to work:
- <c>bin/erl</c> file (obtained from <c>erts-5.1/bin/erl.src</c>)
+ <c>bin/erl</c> file (obtained from <c>erts-5.10.4/bin/erl.src</c>)
and the <c>bin/start.boot</c> file (a copy of <c>plain.boot</c>).</p>
<p>We can also start a distributed system (requires <c>bin/epmd</c>).</p>
<p>To start all applications specified in the original
@@ -208,9 +213,10 @@ os> <input>/usr/local/erl-target/bin/erl -boot /usr/local/erl-target/releases/FI
<c>bin/run_erl</c>, which in turn calls <c>bin/start_erl</c>
(roughly, <c>start_erl</c> is an embedded variant of
<c>erl</c>). </p>
- <p>The shell script <c>start</c> is only an example. You should
- edit it to suite your needs. Typically it is executed when the
- UNIX system boots.</p>
+ <p>The shell script <c>start</c>, which is generated from
+ erts-5.10.4/bin/start.src during installation, is only an
+ example. You should edit it to suite your needs. Typically it is
+ executed when the UNIX system boots.</p>
<p><c>run_erl</c> is a wrapper that provides logging of output from
the run-time system to file. It also provides a simple mechanism
for attaching to the Erlang shell (<c>to_erl</c>).</p>
@@ -218,7 +224,7 @@ os> <input>/usr/local/erl-target/bin/erl -boot /usr/local/erl-target/releases/FI
(<c>"/usr/local/erl-target"</c>), the releases directory
(<c>"/usr/local/erl-target/releases"</c>), and the location of
the <c>start_erl.data</c> file. It reads the run-time system
- version (<c>"5.1"</c>) and release version (<c>"FIRST"</c>) from
+ version (<c>"5.10.4"</c>) and release version (<c>"FIRST"</c>) from
the <c>start_erl.data</c> file, starts the run-time system of the
version found, and provides <c>-boot</c> flag specifying the boot
file of the release version found
@@ -258,6 +264,198 @@ os> <input>/usr/local/erl-target/bin/erl -boot /usr/local/erl-target/releases/FI
</section>
<section>
+ <title>Creating the Next Version</title>
+
+ <p>
+ In this example the <c>pea</c> application has been changed, and
+ so are <c>erts</c>, <c>kernel</c>, <c>stdlib</c> and
+ <c>sasl</c>.
+ </p>
+
+ <p>
+ <em>Step 1.</em> Create the <c>.rel</c> file:
+ </p>
+ <code type="none">
+%% mysystem2.rel
+{release,
+ {"MYSYSTEM", "SECOND"},
+ {erts, "6.0"},
+ [{kernel, "3.0"},
+ {stdlib, "2.0"},
+ {sasl, "2.4"},
+ {pea, "2.0"}]}.</code>
+ <p>
+ <em>Step 2.</em> Create the application upgrade file (see
+ <seealso marker="sasl:appup">appup(4)</seealso>) for <c>pea</c>,
+ for example:
+ </p>
+ <code type="none">
+%% pea.appup
+{"2.0",
+ [{"1.0",[{load_module,pea_lib}]}],
+ [{"1.0",[{load_module,pea_lib}]}]}.</code>
+ <p>
+ <em>Step 3.</em> From the directory where the
+ <c>mysystem2.rel</c> file reside, start the Erlang/OTP system:
+ </p>
+ <pre>
+os> <input>erl -pa /home/user/target_system/myapps/pea-2.0/ebin</input></pre>
+ <p>giving the path to the new version of <c>pea</c>. </p>
+
+ <p>
+ <em>Step 4.</em> Create the release upgrade file (see <seealso
+ marker="sasl:relup">relup(4)</seealso>):
+ </p>
+ <pre>
+1> <input>systools:make_relup("mysystem2",["mysystem"],["mysystem"],[{path,["/home/user/target_system/myapps/pea-1.0/ebin","/my/old/erlang/lib/*/ebin"]}]).</input></pre>
+ <p>
+ where <c>"mysystem"</c> is the base release and
+ <c>"mysystem2"</c> is the release to upgrade to.
+ </p>
+ <p>
+ Note that the <c>path</c> option is used for pointing out the
+ old version of all applications. (The new versions are already
+ in the code path - assuming of course that the erlang node on
+ which this is executed is running the correct version of
+ Erlang/OTP.)
+ </p>
+ <p>
+ <em>Step 5.</em> Create the new release:
+ </p>
+ <pre>
+2> <input>target_system:create("mysystem2").</input></pre>
+ <p>
+ Given that the <c>relup</c> file generated in step 4 above is
+ now located in the current directory, it will automatically be
+ included in the release package.
+ </p>
+ </section>
+
+ <section>
+ <title>Upgrading the Target System</title>
+ <p>
+ This part is done on the target node, and for this example we
+ want the node to be running as an embedded system with the
+ <c>-heart</c> option, allowing automatic restart of the
+ node. See <seealso marker="#start">Starting a Target
+ System</seealso> above for more information.
+ </p>
+ <p>
+ We add <c>-heart</c> to <c>bin/start</c>:
+ </p>
+ <code type="none">
+#!/bin/sh
+ROOTDIR=/usr/local/erl-target/
+
+if [ -z "$RELDIR" ]
+then
+ RELDIR=$ROOTDIR/releases
+fi
+
+START_ERL_DATA=${1:-$RELDIR/start_erl.data}
+
+$ROOTDIR/bin/run_erl -daemon /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart</code>
+ <p>
+ And we use the simplest possible <c>sys.config</c>, which we
+ store in <c>releases/FIRST</c>:
+ </p>
+ <code type="none">
+%% sys.config
+[].</code>
+ <p>
+ Finally, in order to prepare the upgrade, we need to put the new
+ release package in the <c>releases</c> directory of the first
+ target system:
+ </p>
+ <pre>
+os> <input>cp mysystem2.tar.gz /usr/local/erl-target/releases</input></pre>
+ <p>
+ And assuming that the node has been started like this:
+ </p>
+ <pre>
+os> <input>/usr/local/erl-target/bin/start</input></pre>
+ <p>
+ it can be accessed like this:
+ </p>
+ <pre>
+os> <input>/usr/local/erl-target/bin/to_erl /tmp/erlang.pipe.1</input></pre>
+ <p>
+ Also note that logs can be found in
+ <c>/usr/local/erl-target/log</c>. This directory is specified as
+ an argument to <c>run_erl</c>in the start script listed above.
+ </p>
+ <p>
+ <em>Step 1.</em> Unpack the release:
+ </p>
+ <pre>
+1> <input>{ok,Vsn} = release_handler:unpack_release("mysystem2").</input></pre>
+ <p>
+ <em>Step 2.</em> Install the release:
+ </p>
+ <pre>
+2> <input>release_handler:install_release(Vsn).</input>
+<output>{continue_after_restart,"FIRST",[]}
+heart: Tue Apr 1 12:15:10 2014: Erlang has closed.
+heart: Tue Apr 1 12:15:11 2014: Executed "/usr/local/erl-target/bin/start /usr/local/erl-target/releases/new_start_erl.data" -> 0. Terminating.
+[End]</output></pre>
+ <p>
+ The above return value and output after the call to
+ <c>release_handler:install_release/1</c> means that the
+ <c>release_handler</c> has restarted the node by using
+ <c>heart</c>. This will always be done when the upgrade involves
+ a change of <c>erts</c>, <c>kernel</c>, <c>stdlib</c> or
+ <c>sasl</c>. See <seealso marker="upgrade">Upgrade when
+ Erlang/OTP has Changed</seealso> for more infomation about this.
+ </p>
+ <p>
+ The node will be accessable via a new pipe:
+ </p>
+ <pre>
+os> <input>/usr/local/erl-target/bin/to_erl /tmp/erlang.pipe.2</input></pre>
+ <p>
+ Let's see which releases we have in our system:
+ </p>
+ <pre>
+1> <input>release_handler:which_releases().</input>
+<output>[{"MYSYSTEM","SECOND",
+ ["kernel-3.0","stdlib-2.0","sasl-2.4","pea-2.0"],
+ current},
+ {"MYSYSTEM","FIRST",
+ ["kernel-2.16.4","stdlib-1.19.4","sasl-2.3.4","pea-1.0"],
+ permanent}]</output></pre>
+ <p>
+ Our new release, "SECOND", is now the current release, but we
+ can also see that our "FIRST" release is still permanent. This
+ means that if the node would be restarted at this point, it
+ would come up running the "FIRST" release again.
+ </p>
+ <p>
+ <em>Step 3.</em> Make the new release permanent:
+ </p>
+ <pre>
+2> <input>release_handler:make_permanent("SECOND").</input></pre>
+
+ <p>
+ Now look at the releases again:
+ </p>
+
+ <pre>
+3> <input>release_handler:which_releases().</input>
+<output>[{"MYSYSTEM","SECOND",
+ ["kernel-3.0","stdlib-2.0","sasl-2.4","pea-2.0"],
+ permanent},
+ {"MYSYSTEM","FIRST",
+ ["kernel-2.16.4","stdlib-1.19.4","sasl-2.3.4","pea-1.0"],
+ old}]</output></pre>
+
+ <p>
+ Here we see that the new release version is <c>permanent</c>, so
+ it would be safe to restart the node.
+ </p>
+
+ </section>
+
+ <section>
<title>Listing of target_system.erl</title>
<p>This module can also be found in the <c>examples</c> directory
of the <c>sasl</c> application.</p>
diff --git a/system/doc/system_principles/part.xml b/system/doc/system_principles/part.xml
index 915d5aca9b..d05c89fde8 100644
--- a/system/doc/system_principles/part.xml
+++ b/system/doc/system_principles/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,5 +31,6 @@
<xi:include href="system_principles.xml"/>
<xi:include href="error_logging.xml"/>
<xi:include href="create_target.xml"/>
+ <xi:include href="upgrade.xml"/>
+ <xi:include href="versions.xml"/>
</part>
-
diff --git a/system/doc/system_principles/system_principles.xml b/system/doc/system_principles/system_principles.xml
index 4f2202fdd1..70c69b1dab 100644
--- a/system/doc/system_principles/system_principles.xml
+++ b/system/doc/system_principles/system_principles.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -34,13 +34,12 @@
<p>An Erlang runtime system is started with the command <c>erl</c>:</p>
<pre>
% <input>erl</input>
-Erlang (BEAM) emulator version 5.2.3.5 [hipe] [threads:0]
+Erlang/OTP 17 [erts-6.0] [hipe] [smp:8:8]
-Eshell V5.2.3.5 (abort with ^G)
+Eshell V6.0 (abort with ^G)
1> </pre>
<p><c>erl</c> understands a number of command line arguments, see
- <c>erl(1)</c>. A number of them are also described in this
- chapter.</p>
+ <c>erl(1)</c>. A number of them are also described in this chapter.</p>
<p>Application programs can access the values of the command line
arguments by calling one of the functions
<c>init:get_argument(Key)</c>, or <c>init:get_arguments()</c>.
diff --git a/system/doc/system_principles/upgrade.xml b/system/doc/system_principles/upgrade.xml
new file mode 100644
index 0000000000..68e48da0b8
--- /dev/null
+++ b/system/doc/system_principles/upgrade.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2014</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Upgrade when Erlang/OTP has Changed</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2014-02-19</date>
+ <rev></rev>
+ <file>upgrade.xml</file>
+ </header>
+
+ <section>
+ <title>Introduction</title>
+ <p>
+ As of Erlang/OTP 17, most applications deliver a valid
+ application upgrade (<c>appup</c>) file. In earlier releases, a
+ majority of the applications in Erlang/OTP did not support
+ upgrade at all. Many of the applications use the
+ <c>restart_application</c> instruction. These are applications
+ for which it is not crucial to support real soft upgrade, for
+ instance tools and library applications. The
+ <c>restart_application</c> instruction
+ ensures that all modules in the application are reloaded and
+ thereby running the new code.
+ </p>
+ </section>
+
+ <section>
+ <title>Upgrade of core applications</title>
+ <p>
+ The core applications ERTS, Kernel, STDLIB
+ and SASL never allow real soft upgrade, but require the
+ Erlang emulator to be restarted. This is indicated to the
+ <c>release_handler</c> by the upgrade instruction
+ <c>restart_new_emulator</c>. This instruction will always be the
+ very first instruction executed, and it will restart the
+ emulator with the new versions of the above mentioned core
+ applications and the old versions of all other
+ applications. When the node is back up all other upgrade instructions are
+ executed, making sure each application is finally running its
+ new version.
+ </p>
+
+ <p>
+ It might seem strange to do a two-step upgrade instead of
+ just restarting the emulator with the new version of all
+ applications. The reason for this design decision is to allow
+ <c>code_change</c> functions to have side effects, for example changing
+ data on disk. It also makes sure that the upgrade mechanism for
+ non-core applications does not differ depending on whether or not
+ core applications are changed at the same time.
+ </p>
+
+ <p>
+ If, however, the more brutal variant is preferred, it is
+ possible to handwrite the release upgrade file using only the
+ single upgrade instruction <c>restart_emulator</c>. This
+ instruction, in contrast to <c>restart_new_emulator</c>, will
+ cause the emulator to restart with the new versions of
+ <em>all</em> applications.
+ </p>
+
+ <p>
+ <em>Note</em> that if other instructions are included before
+ <c>restart_emulator</c> in the handwritten <c>relup</c> file,
+ they will be executed in the old emulator. This is a big risk
+ since there is no guarantee that new beam code can be loaded
+ into the old emulator. Adding instructions after
+ <c>restart_emulator</c> has no effect as the
+ <c>release_handler</c> will not do any attempt at executing
+ them.
+ </p>
+
+ <p>
+ See <seealso marker="sasl:relup">relup(4)</seealso> for
+ information about the release upgrade file, and <seealso
+ marker="sasl:appup">appup(4)</seealso> for further information
+ about upgrade instructions.
+ </p>
+ </section>
+
+ <section>
+ <title>Applications that still do not allow code upgrade</title>
+ <p>
+ A few applications, for instance HiPE do not support
+ upgrade at all. This is indicated by an application upgrade file
+ containing only <c>{Vsn,[],[]}</c>. Any attempt at creating a release
+ upgrade file with such input will fail.
+ The only way to force an upgrade involving applications like this is to
+ handwrite the <c>relup</c> file, preferably as described above
+ with only the <c>restart_emulator</c> instruction.
+ </p>
+
+ </section>
+</chapter>
diff --git a/system/doc/system_principles/versions.xml b/system/doc/system_principles/versions.xml
new file mode 100644
index 0000000000..c63913d867
--- /dev/null
+++ b/system/doc/system_principles/versions.xml
@@ -0,0 +1,267 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2014</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Versions</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2014-02-19</date>
+ <rev></rev>
+ <file>versions.xml</file>
+ </header>
+ <section><title>OTP Version</title>
+ <p>As of OTP release 17, the OTP release number corresponds to
+ the major part of the OTP version. The OTP version as a concept was
+ introduced in OTP 17. The <seealso marker="#version_scheme">version
+ scheme</seealso> used is described in more detail below.</p>
+
+ <p>OTP of a specific version is a set of applications of specific
+ versions. The application versions identified by an OTP version
+ corresponds to application versions that have been tested together
+ by the Erlang/OTP team at Ericsson AB. An OTP system can however be
+ put together with applications from different OTP versions. Such a
+ combination of application versions has not been tested by the
+ Erlang/OTP team. It is therefore <em>always preferred to use OTP
+ applications from one single OTP version</em>.</p>
+
+ <p>Release candidates have an <c>-rc&lt;N&gt;</c>
+ suffix. The suffix <c>-rc0</c> will be used during development up to
+ the first release candidate.</p>
+
+ <section><title>Retrieving Current OTP Version</title>
+ <p>In an OTP source code tree, the OTP version can be read from
+ the text file <c>&lt;OTP source root&gt;/OTP_VERSION</c>. The
+ absolute path to the file can be constructed by calling
+ <c>filename:join([<seealso marker="kernel:code#root_dir/0">code:root_dir()</seealso>, "OTP_VERSION"])</c>.</p>
+ <p>In an installed OTP development system, the OTP version can be read
+ from the text file <c>&lt;OTP installation root&gt;/releases/&lt;OTP release number&gt;/OTP_VERSION</c>.
+ The absolute path to the file can by constructed by calling
+ <c>filename:join([<seealso marker="kernel:code#root_dir/0">code:root_dir()</seealso>, "releases", <seealso marker="erts:erlang#system_info_otp_release">erlang:system_info(otp_release)</seealso>, "OTP_VERSION"]).</c></p>
+ <p>If the version read from the <c>OTP_VERSION</c> file in a
+ development system has a <c>**</c> suffix, the system has been
+ patched using the <c>otp_patch_apply</c> tool available to
+ licensed customers. In this case, the system consists of application
+ versions from multiple OTP versions. The version preceding the <c>**</c>
+ suffix corresponds to the OTP version of the base system that
+ has been patched. Note that if a development system is updated by
+ other means than <c>otp_patch_apply</c>, the <c>OTP_VERSION</c> file
+ may identify wrong OTP version.</p>
+
+ <p>No <c>OTP_VERSION</c> file will be placed in a
+ <seealso marker="create_target">target system</seealso> created
+ by OTP tools. This since one easily can create a target system
+ where it is hard to even determine the base OTP version. You may,
+ however, place such a file there yourself if you know the OTP
+ version.</p>
+ </section>
+
+ <section><title>OTP Versions Table</title>
+ <p>The text file <c>&lt;OTP source root&gt;/otp_versions.table</c> that
+ is part of the source code contains information about all OTP versions
+ from OTP 17.0 up to current OTP version. Each line contains information
+ about application versions that are part of a specific OTP version, and
+ is on the format:</p>
+<pre>
+&lt;OtpVersion&gt; : &lt;ChangedAppVersions&gt; # &lt;UnchangedAppVersions&gt; :
+</pre>
+ <p><c>&lt;OtpVersion&gt;</c> is on the format <c>OTP-&lt;VSN&gt;</c>, i.e.,
+ the same as the git tag used to identify the source.
+ <c>&lt;ChangedAppVersions&gt;</c> and <c>&lt;UnchangedAppVersions&gt;</c>
+ are space separated lists of application versions on the
+ format <c>&lt;application&gt;-&lt;vsn&gt;</c>.
+ <c>&lt;ChangedAppVersions&gt;</c> corresponds to changed applications
+ with new version numbers in this OTP version, and
+ <c>&lt;UnchangedAppVersions&gt;</c> corresponds to unchanged application
+ versions in this OTP version. Both of them might be empty, although
+ not at the same time. If &lt;ChangedAppVersions&gt; is empty, no changes
+ has been made that change the build result of any application. This could
+ for example be a pure bug fix of the build system. The order of lines
+ is undefined. All white space characters in this file are either space
+ (character 32) or line-break (character 10).</p>
+ <p>Using ordinary UNIX tools like <c>sed</c> and <c>grep</c> one
+ can easily find answers to various questions like:</p>
+ <taglist>
+ <tag>Which OTP versions are <c>kernel-3.0</c> part of?</tag>
+ <item><p><c> $ grep ' kernel-3\.0 ' otp_versions.table</c></p></item>
+ <tag>In which OTP version was <c>kernel-3.0</c> introduced?</tag>
+ <item><p><c> $ sed 's/#.*//;/ kernel-3\.0 /!d' otp_versions.table</c></p></item>
+ </taglist>
+ <p>The above commands give a bit more information than the exact answers,
+ but adequate information when manually searching for answers to these
+ questions.</p>
+ <warning><p>The format of the <c>otp_versions.table</c> might be subject
+ to changes during the OTP 17 release.</p></warning>
+ </section>
+ </section>
+
+ <section><title>Application Version</title>
+ <p>As of OTP 17.0 application versions will use the same
+ <seealso marker="#version_scheme">version scheme</seealso> as the
+ OTP version. Application versions part of a release candidate will
+ however not have an <c>-rc&lt;N&gt;</c> suffix as the OTP version.
+ Also note that a major increment in an application version does not
+ necessarily imply a major increment of the OTP version. This depends
+ on whether the major change in the application is considered as a
+ major change for OTP as a whole or not.</p>
+ </section>
+
+ <marker id="version_scheme"/>
+ <section><title>Version Scheme</title>
+ <note>Note that the version scheme was changed as of OTP 17.0. This implies
+ that application versions used prior to OTP 17.0 do not adhere to this
+ version scheme. <seealso marker="#otp_17_0_app_versions">A list of
+ application versions used in OTP 17.0</seealso> can be found
+ at the end of this document.</note>
+
+ <p>In the normal case, a version will be constructed as
+ <c>&lt;Major&gt;.&lt;Minor&gt;.&lt;Patch&gt;</c> where <c>&lt;Major&gt;</c>
+ is the most significant part. However, more dot separated parts than
+ this may exist. The dot separated parts consists of non-negative integers.
+ If all parts less significant than <c>&lt;Minor&gt;</c> equals <c>0</c>,
+ they are omitted. The three normal parts
+ <c>&lt;Major&gt;.&lt;Minor&gt;.&lt;Patch&gt;</c> will be changed as
+ follows:</p>
+ <taglist>
+ <tag><c>&lt;Major&gt;</c></tag><item>Increased when major changes,
+ including incompatibilities, have been made.</item>
+ <tag><c>&lt;Minor&gt;</c></tag><item>Increased when new functionality
+ has been added.</item>
+ <tag><c>&lt;Patch&gt;</c></tag><item>Increased when pure bug fixes
+ have been made.</item>
+ </taglist>
+ <p>When a part in the version number is increased, all less significant
+ parts are set to <c>0</c>.</p>
+
+ <p>An application version or an OTP version identifies source code
+ versions. That is, it does not imply anything about how the application
+ or OTP has been built.</p>
+
+ <section><title>Order of Versions</title>
+ <p>Version numbers in general are only partially ordered. However,
+ normal version numbers (with three parts) as of OTP 17.0 have a total
+ or linear order. This applies both to normal OTP versions and
+ normal application versions.</p>
+
+ <p>When comparing two version numbers that have an order, one
+ compare each part as ordinary integers from the most
+ significant part towards less significant parts. The order is
+ defined by the first parts of the same significance that
+ differ. An OTP version with a larger version include all
+ changes that that are part of a smaller OTP version. The same
+ goes for application versions.</p>
+
+ <p>In the general case, versions may have more than three parts. In
+ this case the versions are only partially ordered. Note that such
+ versions are only used in exceptional cases. When an extra
+ part (out of the normal three parts) is added to a version number,
+ a new branch of versions is made. The new branch has a linear
+ order against the base version. However, versions on different
+ branches have no order. Since they have no order, we
+ only know that they all include what is included in their
+ closest common ancestor. When branching multiple times from the
+ same base version, <c>0</c> parts are added between the base
+ version and the least significant <c>1</c> part until a unique
+ version is found. Versions that have an order can be compared
+ as described in the paragraph above.</p>
+
+ <p>An example of branched versions: The version <c>6.0.2.1</c>
+ is a branched version from the base version <c>6.0.2</c>.
+ Versions on the form <c>6.0.2.&lt;X&gt;</c> can be compared
+ with normal versions smaller than or equal to <c>6.0.2</c>,
+ and other versions on the form <c>6.0.2.&lt;X&gt;</c>. The
+ version <c>6.0.2.1</c> will include all changes in
+ <c>6.0.2</c>. However, <c>6.0.3</c> will most likely
+ <em>not</em> include all changes in <c>6.0.2.1</c> (note that
+ these versions have no order). A second branched version from the base
+ version <c>6.0.2</c> will be version <c>6.0.2.0.1</c>, and a
+ third branched version will be <c>6.0.2.0.0.1</c>.</p>
+ </section>
+ </section>
+
+ <marker id="otp_17_0_app_versions"/>
+ <section><title>OTP 17.0 Application Versions</title>
+ <p>The following application versions were part of OTP 17.0. If
+ the normal part of an applications version number compares
+ as smaller than the corresponding application version in this list,
+ the version number does not adhere to the version scheme introduced
+ in OTP 17.0 and should be considered as not having an order against
+ versions used as of OTP 17.0.</p>
+ <list>
+ <item><c>asn1-3.0</c></item>
+ <item><c>common_test-1.8</c></item>
+ <item><c>compiler-5.0</c></item>
+ <item><c>cosEvent-2.1.15</c></item>
+ <item><c>cosEventDomain-1.1.14</c></item>
+ <item><c>cosFileTransfer-1.1.16</c></item>
+ <item><c>cosNotification-1.1.21</c></item>
+ <item><c>cosProperty-1.1.17</c></item>
+ <item><c>cosTime-1.1.14</c></item>
+ <item><c>cosTransactions-1.2.14</c></item>
+ <item><c>crypto-3.3</c></item>
+ <item><c>debugger-4.0</c></item>
+ <item><c>dialyzer-2.7</c></item>
+ <item><c>diameter-1.6</c></item>
+ <item><c>edoc-0.7.13</c></item>
+ <item><c>eldap-1.0.3</c></item>
+ <item><c>erl_docgen-0.3.5</c></item>
+ <item><c>erl_interface-3.7.16</c></item>
+ <item><c>erts-6.0</c></item>
+ <item><c>et-1.5</c></item>
+ <item><c>eunit-2.2.7</c></item>
+ <item><c>gs-1.5.16</c></item>
+ <item><c>hipe-3.10.3</c></item>
+ <item><c>ic-4.3.5</c></item>
+ <item><c>inets-5.10</c></item>
+ <item><c>jinterface-1.5.9</c></item>
+ <item><c>kernel-3.0</c></item>
+ <item><c>megaco-3.17.1</c></item>
+ <item><c>mnesia-4.12</c></item>
+ <item><c>observer-2.0</c></item>
+ <item><c>odbc-2.10.20</c></item>
+ <item><c>orber-3.6.27</c></item>
+ <item><c>os_mon-2.2.15</c></item>
+ <item><c>ose-1.0</c></item>
+ <item><c>otp_mibs-1.0.9</c></item>
+ <item><c>parsetools-2.0.11</c></item>
+ <item><c>percept-0.8.9</c></item>
+ <item><c>public_key-0.22</c></item>
+ <item><c>reltool-0.6.5</c></item>
+ <item><c>runtime_tools-1.8.14</c></item>
+ <item><c>sasl-2.4</c></item>
+ <item><c>snmp-4.25.1</c></item>
+ <item><c>ssh-3.0.1</c></item>
+ <item><c>ssl-5.3.4</c></item>
+ <item><c>stdlib-2.0</c></item>
+ <item><c>syntax_tools-1.6.14</c></item>
+ <item><c>test_server-3.7</c></item>
+ <item><c>tools-2.6.14</c></item>
+ <item><c>typer-0.9.6</c></item>
+ <item><c>webtool-0.8.10</c></item>
+ <item><c>wx-1.2</c></item>
+ <item><c>xmerl-1.3.7</c></item>
+ </list>
+ </section>
+</chapter>
+
diff --git a/system/doc/system_principles/xmlfiles.mk b/system/doc/system_principles/xmlfiles.mk
index 4cbc00ed52..c60ffbad28 100644
--- a/system/doc/system_principles/xmlfiles.mk
+++ b/system/doc/system_principles/xmlfiles.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-2014. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -19,4 +19,6 @@
SYSTEM_PRINCIPLES_CHAPTER_FILES = \
system_principles.xml \
error_logging.xml \
- create_target.xml
+ create_target.xml \
+ upgrade.xml \
+ versions.xml
diff --git a/xcomp/erl-xcomp-arm-android.conf b/xcomp/erl-xcomp-arm-android.conf
new file mode 100644
index 0000000000..4fd1f270d1
--- /dev/null
+++ b/xcomp/erl-xcomp-arm-android.conf
@@ -0,0 +1,264 @@
+## -*-shell-script-*-
+##
+## %CopyrightBegin%
+##
+## Copyright Ericsson AB 2009-2010. All Rights Reserved.
+##
+## The contents of this file are subject to the Erlang Public License,
+## Version 1.1, (the "License"); you may not use this file except in
+## compliance with the License. You should have received a copy of the
+## Erlang Public License along with this software. If not, it can be
+## retrieved online at http://www.erlang.org/.
+##
+## Software distributed under the License is distributed on an "AS IS"
+## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+## the License for the specific language governing rights and limitations
+## under the License.
+##
+## %CopyrightEnd%
+##
+## File: erl-xcomp.conf.template
+## Author:
+##
+## -----------------------------------------------------------------------------
+## When cross compiling Erlang/OTP using `otp_build', copy this file and set
+## the variables needed below. Then pass the path to the copy of this file as
+## an argument to `otp_build' in the configure stage:
+## `otp_build configure --xcomp-conf=<FILE>'
+## -----------------------------------------------------------------------------
+
+## Note that you cannot define arbitrary variables in a cross compilation
+## configuration file. Only the ones listed below will be guaranteed to be
+## visible throughout the whole execution of all `configure' scripts. Other
+## variables needs to be defined as arguments to `configure' or exported in
+## the environment.
+
+## -- Variables for `otp_build' Only -------------------------------------------
+
+## Variables in this section are only used, when configuring Erlang/OTP for
+## cross compilation using `$ERL_TOP/otp_build configure'.
+
+## *NOTE*! These variables currently have *no* effect if you configure using
+## the `configure' script directly.
+
+# * `erl_xcomp_build' - The build system used. This value will be passed as
+# `--build=$erl_xcomp_build' argument to the `configure' script. It does
+# not have to be a full `CPU-VENDOR-OS' triplet, but can be. The full
+# `CPU-VENDOR-OS' triplet will be created by
+# `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_build'. If set to `guess',
+# the build system will be guessed using
+# `$ERL_TOP/erts/autoconf/config.guess'.
+erl_xcomp_build=guess
+
+# * `erl_xcomp_host' - Cross host/target system to build for. This value will
+# be passed as `--host=$erl_xcomp_host' argument to the `configure' script.
+# It does not have to be a full `CPU-VENDOR-OS' triplet, but can be. The
+# full `CPU-VENDOR-OS' triplet will be created by
+# `$ERL_TOP/erts/autoconf/config.sub $erl_xcomp_host'.
+erl_xcomp_host=arm-linux-androideabi
+
+# * `erl_xcomp_configure_flags' - Extra configure flags to pass to the
+# `configure' script.
+erl_xcomp_configure_flags="--disable-hipe --without-termcap"
+
+
+## -- Cross Compiler and Other Tools -------------------------------------------
+
+##
+##
+NDK_SYSROOT=$NDK_ROOT/platforms/$NDK_PLAT/arch-arm
+
+## If the cross compilation tools are prefixed by `<HOST>-' you probably do
+## not need to set these variables (where `<HOST>' is what has been passed as
+## `--host=<HOST>' argument to `configure').
+
+## All variables in this section can also be used when native compiling.
+
+# * `CC' - C compiler.
+CC="arm-linux-androideabi-gcc --sysroot=$NDK_SYSROOT"
+
+# * `CFLAGS' - C compiler flags.
+CFLAGS="-static -march=armv7-a -msoft-float -mthumb"
+
+# * `STATIC_CFLAGS' - Static C compiler flags.
+#STATIC_CFLAGS=
+
+# * `CFLAG_RUNTIME_LIBRARY_PATH' - This flag should set runtime library
+# search path for the shared libraries. Note that this actually is a
+# linker flag, but it needs to be passed via the compiler.
+#CFLAG_RUNTIME_LIBRARY_PATH=
+
+# * `CPP' - C pre-processor.
+CPP="arm-linux-androideabi-cpp --sysroot=$NDK_SYSROOT"
+
+# * `CPPFLAGS' - C pre-processor flags.
+CPPFLAGS="-static -march=armv7-a -msoft-float -mthumb"
+
+# * `CXX' - C++ compiler.
+CXX="arm-linux-androideabi-c++ --sysroot=$NDK_SYSROOT"
+
+# * `CXXFLAGS' - C++ compiler flags.
+CXXFLAGS="-static -march=armv7-a -msoft-float -mthumb"
+
+# * `LD' - Linker.
+#LD=
+
+# * `LDFLAGS' - Linker flags.
+LDFLAGS="-static -march=armv7-a -msoft-float -mthumb"
+
+# * `LIBS' - Libraries.
+#LIBS=
+
+## -- *D*ynamic *E*rlang *D*river Linking --
+
+## *NOTE*! Either set all or none of the `DED_LD*' variables.
+
+# * `DED_LD' - Linker for Dynamically loaded Erlang Drivers.
+#DED_LD=
+
+# * `DED_LDFLAGS' - Linker flags to use with `DED_LD'.
+#DED_LDFLAGS=
+
+# * `DED_LD_FLAG_RUNTIME_LIBRARY_PATH' - This flag should set runtime library
+# search path for shared libraries when linking with `DED_LD'.
+#DED_LD_FLAG_RUNTIME_LIBRARY_PATH=
+
+## -- Large File Support --
+
+## *NOTE*! Either set all or none of the `LFS_*' variables.
+
+# * `LFS_CFLAGS' - Large file support C compiler flags.
+#LFS_CFLAGS=
+
+# * `LFS_LDFLAGS' - Large file support linker flags.
+#LFS_LDFLAGS=
+
+# * `LFS_LIBS' - Large file support libraries.
+#LFS_LIBS=
+
+## -- Other Tools --
+
+# * `RANLIB' - `ranlib' archive index tool.
+#RANLIB=
+
+# * `AR' - `ar' archiving tool.
+#AR=
+
+# * `GETCONF' - `getconf' system configuration inspection tool. `getconf' is
+# currently used for finding out large file support flags to use, and
+# on Linux systems for finding out if we have an NPTL thread library or
+# not.
+#GETCONF=
+
+## -- Cross System Root Locations ----------------------------------------------
+
+# * `erl_xcomp_sysroot' - The absolute path to the system root of the cross
+# compilation environment. Currently, the `crypto', `odbc', `ssh' and
+# `ssl' applications need the system root. These applications will be
+# skipped if the system root has not been set. The system root might be
+# needed for other things too. If this is the case and the system root
+# has not been set, `configure' will fail and request you to set it.
+erl_xcomp_sysroot="$NDK_SYSROOT"
+
+
+# * `erl_xcomp_isysroot' - The absolute path to the system root for includes
+# of the cross compilation environment. If not set, this value defaults
+# to `$erl_xcomp_sysroot', i.e., only set this value if the include system
+# root path is not the same as the system root path.
+#erl_xcomp_isysroot=
+
+## -- Optional Feature, and Bug Tests ------------------------------------------
+
+## These tests cannot (always) be done automatically when cross compiling. You
+## usually do not need to set these variables. Only set these if you really
+## know what you are doing.
+
+## Note that some of these values will override results of tests performed
+## by `configure', and some will not be used until `configure' is sure that
+## it cannot figure the result out.
+
+## The `configure' script will issue a warning when a default value is used.
+## When a variable has been set, no warning will be issued.
+
+# * `erl_xcomp_after_morecore_hook' - `yes|no'. Defaults to `no'. If `yes',
+# the target system must have a working `__after_morecore_hook' that can be
+# used for tracking used `malloc()' implementations core memory usage.
+# This is currently only used by unsupported features.
+#erl_xcomp_after_morecore_hook=
+
+# * `erl_xcomp_bigendian' - `yes|no'. No default. If `yes', the target system
+# must be big endian. If `no', little endian. This can often be
+# automatically detected, but not always. If not automatically detected,
+# `configure' will fail unless this variable is set. Since no default
+# value is used, `configure' will try to figure this out automatically.
+#erl_xcomp_bigendian=
+
+# * `erl_xcomp_clock_gettime_cpu_time' - `yes|no'. Defaults to `no'. If `yes',
+# the target system must have a working `clock_gettime()' implementation
+# that can be used for retrieving process CPU time.
+#erl_xcomp_clock_gettime_cpu_time=
+
+# * `erl_xcomp_getaddrinfo' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have a working `getaddrinfo()' implementation that can
+# handle both IPv4 and IPv6.
+#erl_xcomp_getaddrinfo=
+
+# * `erl_xcomp_gethrvtime_procfs_ioctl' - `yes|no'. Defaults to `no'. If `yes',
+# the target system must have a working `gethrvtime()' implementation and
+# is used with procfs `ioctl()'.
+#erl_xcomp_gethrvtime_procfs_ioctl=
+
+# * `erl_xcomp_dlsym_brk_wrappers' - `yes|no'. Defaults to `no'. If `yes', the
+# target system must have a working `dlsym(RTLD_NEXT, <S>)' implementation
+# that can be used on `brk' and `sbrk' symbols used by the `malloc()'
+# implementation in use, and by this track the `malloc()' implementations
+# core memory usage. This is currently only used by unsupported features.
+#erl_xcomp_dlsym_brk_wrappers=
+
+# * `erl_xcomp_kqueue' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have a working `kqueue()' implementation that returns a file
+# descriptor which can be used by `poll()' and/or `select()'. If `no' and
+# the target system has not got `epoll()' or `/dev/poll', the kernel-poll
+# feature will be disabled.
+#erl_xcomp_kqueue=
+
+# * `erl_xcomp_linux_clock_gettime_correction' - `yes|no'. Defaults to `yes' on
+# Linux; otherwise, `no'. If `yes', `clock_gettime(CLOCK_MONOTONIC, _)' on
+# the target system must work. This variable is recommended to be set to
+# `no' on Linux systems with kernel versions less than 2.6.
+#erl_xcomp_linux_clock_gettime_correction=
+
+# * `erl_xcomp_linux_nptl' - `yes|no'. Defaults to `yes' on Linux; otherwise,
+# `no'. If `yes', the target system must have NPTL (Native POSIX Thread
+# Library). Older Linux systems have LinuxThreads instead of NPTL (Linux
+# kernel versions typically less than 2.6).
+#erl_xcomp_linux_nptl=
+
+# * `erl_xcomp_linux_usable_sigaltstack' - `yes|no'. Defaults to `yes' on Linux;
+# otherwise, `no'. If `yes', `sigaltstack()' must be usable on the target
+# system. `sigaltstack()' on Linux kernel versions less than 2.4 are
+# broken.
+#erl_xcomp_linux_usable_sigaltstack=
+
+# * `erl_xcomp_linux_usable_sigusrx' - `yes|no'. Defaults to `yes'. If `yes',
+# the `SIGUSR1' and `SIGUSR2' signals must be usable by the ERTS. Old
+# LinuxThreads thread libraries (Linux kernel versions typically less than
+# 2.2) used these signals and made them unusable by the ERTS.
+#erl_xcomp_linux_usable_sigusrx=
+
+# * `erl_xcomp_poll' - `yes|no'. Defaults to `no' on Darwin/MacOSX; otherwise,
+# `yes'. If `yes', the target system must have a working `poll()'
+# implementation that also can handle devices. If `no', `select()' will be
+# used instead of `poll()'.
+#erl_xcomp_poll=
+
+# * `erl_xcomp_putenv_copy' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have a `putenv()' implementation that stores a copy of the
+# key/value pair.
+#erl_xcomp_putenv_copy=
+
+# * `erl_xcomp_reliable_fpe' - `yes|no'. Defaults to `no'. If `yes', the target
+# system must have reliable floating point exceptions.
+#erl_xcomp_reliable_fpe=
+
+## -----------------------------------------------------------------------------
diff --git a/xcomp/erl-xcomp-powerpc-ose5.conf b/xcomp/erl-xcomp-powerpc-ose5.conf
index 60b75b36c1..bf49b32827 100644
--- a/xcomp/erl-xcomp-powerpc-ose5.conf
+++ b/xcomp/erl-xcomp-powerpc-ose5.conf
@@ -338,8 +338,14 @@ erl_xcomp_ose_LM_ELF_SIZE="${OSEROOT}/bin/${HOST}/lm_elf_size"
# * `erl_xcomp_ose_LM_LCF` - OSE load module linker configuration file
erl_xcomp_ose_LM_LCF="${ERL_TOP}/erts/emulator/sys/ose/gcc_${GCCVERSION}_lm_ppc.lcf"
-# * `erl_xcomp_ose_LM_CONF` - OSE load module default configuration file
-erl_xcomp_ose_LM_CONF="${ERL_TOP}/erts/emulator/sys/ose/default.lmconf"
+# * `erl_xcomp_ose_BEAM_LM_CONF` - beam OSE load module configuration file
+erl_xcomp_ose_BEAM_LM_CONF="${ERL_TOP}/erts/emulator/sys/ose/beam.lmconf"
+
+# * `erl_xcomp_ose_RUN_ERL_LM_CONF` - run_erl_lm OSE load module configuration file
+erl_xcomp_ose_RUN_ERL_LM_CONF="${ERL_TOP}/erts/etc/ose/etc.lmconf"
+
+# * `erl_xcomp_ose_EPMD_LM_CONF` - epmd OSE load module configuration file
+erl_xcomp_ose_EPMD_LM_CONF="${ERL_TOP}/erts/etc/ose/etc.lmconf"
# * `erl_xcomp_ose_CONFD` - OSE confd source file
erl_xcomp_ose_CONFD="${OSEROOT}/src/ose_confd.c"
diff --git a/xcomp/erl-xcomp-vars.sh b/xcomp/erl-xcomp-vars.sh
index c5e245b4b6..5e55c3af1b 100644
--- a/xcomp/erl-xcomp-vars.sh
+++ b/xcomp/erl-xcomp-vars.sh
@@ -26,4 +26,4 @@
# and precious variables in $ERL_TOP/erts/aclocal.m4.
#
-erl_xcomp_vars="erl_xcomp_sysroot erl_xcomp_isysroot erl_xcomp_bigendian erl_xcomp_double_middle_endian erl_xcomp_linux_clock_gettime_correction erl_xcomp_linux_nptl erl_xcomp_linux_usable_sigusrx erl_xcomp_linux_usable_sigaltstack erl_xcomp_poll erl_xcomp_kqueue erl_xcomp_putenv_copy erl_xcomp_reliable_fpe erl_xcomp_getaddrinfo erl_xcomp_gethrvtime_procfs_ioctl erl_xcomp_clock_gettime_cpu_time erl_xcomp_after_morecore_hook erl_xcomp_dlsym_brk_wrappers erl_xcomp_posix_memalign erl_xcomp_ose_ldflags_pass1 erl_xcomp_ose_ldflags_pass2 erl_xcomp_ose_OSEROOT erl_xcomp_ose_STRIP erl_xcomp_ose_LM_POST_LINK erl_xcomp_ose_LM_SET_CONF erl_xcomp_ose_LM_GET_CONF erl_xcomp_ose_LM_ELF_SIZE erl_xcomp_ose_LM_LCF erl_xcomp_ose_LM_CONF erl_xcomp_ose_CONFD erl_xcomp_ose_CRT0_LM"
+erl_xcomp_vars="erl_xcomp_sysroot erl_xcomp_isysroot erl_xcomp_bigendian erl_xcomp_double_middle_endian erl_xcomp_linux_clock_gettime_correction erl_xcomp_linux_nptl erl_xcomp_linux_usable_sigusrx erl_xcomp_linux_usable_sigaltstack erl_xcomp_poll erl_xcomp_kqueue erl_xcomp_putenv_copy erl_xcomp_reliable_fpe erl_xcomp_getaddrinfo erl_xcomp_gethrvtime_procfs_ioctl erl_xcomp_clock_gettime_cpu_time erl_xcomp_after_morecore_hook erl_xcomp_dlsym_brk_wrappers erl_xcomp_posix_memalign erl_xcomp_ose_ldflags_pass1 erl_xcomp_ose_ldflags_pass2 erl_xcomp_ose_OSEROOT erl_xcomp_ose_STRIP erl_xcomp_ose_LM_POST_LINK erl_xcomp_ose_LM_SET_CONF erl_xcomp_ose_LM_GET_CONF erl_xcomp_ose_LM_ELF_SIZE erl_xcomp_ose_LM_LCF erl_xcomp_ose_BEAM_LM_CONF erl_xcomp_ose_EPMD_LM_CONF erl_xcomp_ose_RUN_ERL_LM_CONF erl_xcomp_ose_CONFD erl_xcomp_ose_CRT0_LM"